summaryrefslogtreecommitdiff
path: root/src/mscorlib/shared/System
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/shared/System')
-rw-r--r--src/mscorlib/shared/System/Action.cs35
-rw-r--r--src/mscorlib/shared/System/ApplicationException.cs56
-rw-r--r--src/mscorlib/shared/System/ArgumentException.cs97
-rw-r--r--src/mscorlib/shared/System/ArgumentNullException.cs53
-rw-r--r--src/mscorlib/shared/System/ArithmeticException.cs51
-rw-r--r--src/mscorlib/shared/System/ArrayTypeMismatchException.cs51
-rw-r--r--src/mscorlib/shared/System/AssemblyLoadEventArgs.cs18
-rw-r--r--src/mscorlib/shared/System/AssemblyLoadEventHandler.cs8
-rw-r--r--src/mscorlib/shared/System/AsyncCallback.cs17
-rw-r--r--src/mscorlib/shared/System/AttributeTargets.cs36
-rw-r--r--src/mscorlib/shared/System/AttributeUsageAttribute.cs58
-rw-r--r--src/mscorlib/shared/System/Buffers/ArrayPool.cs100
-rw-r--r--src/mscorlib/shared/System/Buffers/ConfigurableArrayPool.cs265
-rw-r--r--src/mscorlib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs292
-rw-r--r--src/mscorlib/shared/System/Buffers/Utilities.cs35
-rw-r--r--src/mscorlib/shared/System/CLSCompliantAttribute.cs34
-rw-r--r--src/mscorlib/shared/System/Char.cs1122
-rw-r--r--src/mscorlib/shared/System/CharEnumerator.cs80
-rw-r--r--src/mscorlib/shared/System/Collections/DictionaryEntry.cs58
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/ICollection.cs35
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/IComparer.cs20
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/IDictionary.cs51
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/IEnumerable.cs21
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/IEnumerator.cs26
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/IEqualityComparer.cs18
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/IList.cs37
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/IReadOnlyCollection.cs16
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/IReadOnlyDictionary.cs20
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/IReadOnlyList.cs16
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs33
-rw-r--r--src/mscorlib/shared/System/Collections/Generic/KeyValuePair.cs82
-rw-r--r--src/mscorlib/shared/System/Collections/ICollection.cs70
-rw-r--r--src/mscorlib/shared/System/Collections/IComparer.cs22
-rw-r--r--src/mscorlib/shared/System/Collections/IDictionary.cs61
-rw-r--r--src/mscorlib/shared/System/Collections/IDictionaryEnumerator.cs68
-rw-r--r--src/mscorlib/shared/System/Collections/IEnumerable.cs18
-rw-r--r--src/mscorlib/shared/System/Collections/IEnumerator.cs41
-rw-r--r--src/mscorlib/shared/System/Collections/IEqualityComparer.cs16
-rw-r--r--src/mscorlib/shared/System/Collections/IList.cs60
-rw-r--r--src/mscorlib/shared/System/Collections/IStructuralComparable.cs13
-rw-r--r--src/mscorlib/shared/System/Collections/IStructuralEquatable.cs12
-rw-r--r--src/mscorlib/shared/System/ComponentModel/DefaultValueAttribute.cs228
-rw-r--r--src/mscorlib/shared/System/ComponentModel/EditorBrowsableAttribute.cs48
-rw-r--r--src/mscorlib/shared/System/Configuration/Assemblies/AssemblyHashAlgorithm.cs16
-rw-r--r--src/mscorlib/shared/System/Configuration/Assemblies/AssemblyVersionCompatibility.cs13
-rw-r--r--src/mscorlib/shared/System/Convert.cs3031
-rw-r--r--src/mscorlib/shared/System/CurrentSystemTimeZone.cs199
-rw-r--r--src/mscorlib/shared/System/DBNull.cs119
-rw-r--r--src/mscorlib/shared/System/DataMisalignedException.cs39
-rw-r--r--src/mscorlib/shared/System/DateTime.cs1516
-rw-r--r--src/mscorlib/shared/System/DateTimeKind.cs17
-rw-r--r--src/mscorlib/shared/System/DateTimeOffset.cs921
-rw-r--r--src/mscorlib/shared/System/DayOfWeek.cs27
-rw-r--r--src/mscorlib/shared/System/DefaultBinder.cs1201
-rw-r--r--src/mscorlib/shared/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs39
-rw-r--r--src/mscorlib/shared/System/Diagnostics/ConditionalAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Debug.cs323
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/ActivityTracker.cs665
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventActivityOptions.cs39
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs436
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventDescriptor.cs209
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs1207
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs6942
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/EventSourceException.cs53
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/StubEnvironment.cs381
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs63
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs127
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs25
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs318
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs17
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs30
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs64
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs146
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs76
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs130
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs25
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs155
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs321
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs130
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs231
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs96
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs79
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs39
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs252
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs39
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs297
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs727
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs104
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs349
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs890
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs28
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs262
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs370
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs209
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs103
-rw-r--r--src/mscorlib/shared/System/Diagnostics/Tracing/Winmeta.cs196
-rw-r--r--src/mscorlib/shared/System/DivideByZeroException.cs41
-rw-r--r--src/mscorlib/shared/System/DuplicateWaitObjectException.cs65
-rw-r--r--src/mscorlib/shared/System/EntryPointNotFoundException.cs42
-rw-r--r--src/mscorlib/shared/System/EventArgs.cs19
-rw-r--r--src/mscorlib/shared/System/EventHandler.cs14
-rw-r--r--src/mscorlib/shared/System/ExecutionEngineException.cs47
-rw-r--r--src/mscorlib/shared/System/FieldAccessException.cs39
-rw-r--r--src/mscorlib/shared/System/FlagsAttribute.cs22
-rw-r--r--src/mscorlib/shared/System/FormatException.cs41
-rw-r--r--src/mscorlib/shared/System/FormattableString.cs81
-rw-r--r--src/mscorlib/shared/System/Globalization/CalendarAlgorithmType.cs18
-rw-r--r--src/mscorlib/shared/System/Globalization/CalendarWeekRule.cs16
-rw-r--r--src/mscorlib/shared/System/Globalization/CalendricalCalculationsHelper.cs412
-rw-r--r--src/mscorlib/shared/System/Globalization/ChineseLunisolarCalendar.cs390
-rw-r--r--src/mscorlib/shared/System/Globalization/CultureNotFoundException.cs120
-rw-r--r--src/mscorlib/shared/System/Globalization/CultureTypes.cs27
-rw-r--r--src/mscorlib/shared/System/Globalization/DateTimeFormat.cs1206
-rw-r--r--src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs3028
-rw-r--r--src/mscorlib/shared/System/Globalization/DateTimeFormatInfoScanner.cs739
-rw-r--r--src/mscorlib/shared/System/Globalization/DateTimeParse.cs5672
-rw-r--r--src/mscorlib/shared/System/Globalization/DateTimeStyles.cs49
-rw-r--r--src/mscorlib/shared/System/Globalization/DaylightTime.cs50
-rw-r--r--src/mscorlib/shared/System/Globalization/DigitShapes.cs13
-rw-r--r--src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs710
-rw-r--r--src/mscorlib/shared/System/Globalization/GregorianCalendarTypes.cs19
-rw-r--r--src/mscorlib/shared/System/Globalization/HebrewCalendar.cs1129
-rw-r--r--src/mscorlib/shared/System/Globalization/HebrewNumber.cs457
-rw-r--r--src/mscorlib/shared/System/Globalization/HijriCalendar.cs677
-rw-r--r--src/mscorlib/shared/System/Globalization/InternalGlobalizationHelper.cs48
-rw-r--r--src/mscorlib/shared/System/Globalization/JapaneseCalendar.cs409
-rw-r--r--src/mscorlib/shared/System/Globalization/JapaneseLunisolarCalendar.cs305
-rw-r--r--src/mscorlib/shared/System/Globalization/JulianCalendar.cs443
-rw-r--r--src/mscorlib/shared/System/Globalization/KoreanCalendar.cs266
-rw-r--r--src/mscorlib/shared/System/Globalization/KoreanLunisolarCalendar.cs1323
-rw-r--r--src/mscorlib/shared/System/Globalization/LocaleData.Unix.cs4572
-rw-r--r--src/mscorlib/shared/System/Globalization/NumberStyles.cs65
-rw-r--r--src/mscorlib/shared/System/Globalization/PersianCalendar.cs606
-rw-r--r--src/mscorlib/shared/System/Globalization/SortVersion.cs98
-rw-r--r--src/mscorlib/shared/System/Globalization/TaiwanCalendar.cs285
-rw-r--r--src/mscorlib/shared/System/Globalization/TaiwanLunisolarCalendar.cs325
-rw-r--r--src/mscorlib/shared/System/Globalization/ThaiBuddhistCalendar.cs236
-rw-r--r--src/mscorlib/shared/System/Globalization/TimeSpanStyles.cs13
-rw-r--r--src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs865
-rw-r--r--src/mscorlib/shared/System/Globalization/UnicodeCategory.cs40
-rw-r--r--src/mscorlib/shared/System/IAsyncResult.cs30
-rw-r--r--src/mscorlib/shared/System/ICloneable.cs11
-rw-r--r--src/mscorlib/shared/System/IComparable.cs37
-rw-r--r--src/mscorlib/shared/System/IConvertible.cs63
-rw-r--r--src/mscorlib/shared/System/ICustomFormatter.cs24
-rw-r--r--src/mscorlib/shared/System/IDisposable.cs60
-rw-r--r--src/mscorlib/shared/System/IEquatable.cs14
-rw-r--r--src/mscorlib/shared/System/IFormatProvider.cs23
-rw-r--r--src/mscorlib/shared/System/IFormattable.cs15
-rw-r--r--src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs41
-rw-r--r--src/mscorlib/shared/System/IO/EndOfStreamException.cs35
-rw-r--r--src/mscorlib/shared/System/IO/Error.cs49
-rw-r--r--src/mscorlib/shared/System/IO/FileAccess.cs29
-rw-r--r--src/mscorlib/shared/System/IO/FileLoadException.cs102
-rw-r--r--src/mscorlib/shared/System/IO/FileMode.cs38
-rw-r--r--src/mscorlib/shared/System/IO/FileNotFoundException.cs114
-rw-r--r--src/mscorlib/shared/System/IO/FileOptions.cs33
-rw-r--r--src/mscorlib/shared/System/IO/FileShare.cs45
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.Linux.cs30
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.OSX.cs19
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.Unix.cs933
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.Win32.cs77
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.WinRT.cs78
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.Windows.cs1717
-rw-r--r--src/mscorlib/shared/System/IO/FileStream.cs684
-rw-r--r--src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs222
-rw-r--r--src/mscorlib/shared/System/IO/Path.Unix.cs215
-rw-r--r--src/mscorlib/shared/System/IO/Path.Windows.cs155
-rw-r--r--src/mscorlib/shared/System/IO/Path.cs574
-rw-r--r--src/mscorlib/shared/System/IO/PathHelper.Windows.cs398
-rw-r--r--src/mscorlib/shared/System/IO/PathInternal.Unix.cs104
-rw-r--r--src/mscorlib/shared/System/IO/PathInternal.Windows.StringBuffer.cs93
-rw-r--r--src/mscorlib/shared/System/IO/PathInternal.Windows.cs442
-rw-r--r--src/mscorlib/shared/System/IO/PathInternal.cs171
-rw-r--r--src/mscorlib/shared/System/IO/PathTooLongException.cs37
-rw-r--r--src/mscorlib/shared/System/IO/SeekOrigin.cs16
-rw-r--r--src/mscorlib/shared/System/IO/StreamHelpers.CopyValidation.cs46
-rw-r--r--src/mscorlib/shared/System/IO/Win32Marshal.cs109
-rw-r--r--src/mscorlib/shared/System/IObservable.cs11
-rw-r--r--src/mscorlib/shared/System/IObserver.cs13
-rw-r--r--src/mscorlib/shared/System/IProgress.cs15
-rw-r--r--src/mscorlib/shared/System/IndexOutOfRangeException.cs41
-rw-r--r--src/mscorlib/shared/System/InsufficientExecutionStackException.cs32
-rw-r--r--src/mscorlib/shared/System/InvalidCastException.cs44
-rw-r--r--src/mscorlib/shared/System/InvalidOperationException.cs42
-rw-r--r--src/mscorlib/shared/System/InvalidProgramException.cs41
-rw-r--r--src/mscorlib/shared/System/InvalidTimeZoneException.cs28
-rw-r--r--src/mscorlib/shared/System/Lazy.cs561
-rw-r--r--src/mscorlib/shared/System/MarshalByRefObject.cs30
-rw-r--r--src/mscorlib/shared/System/MemberAccessException.cs48
-rw-r--r--src/mscorlib/shared/System/MethodAccessException.cs39
-rw-r--r--src/mscorlib/shared/System/MidpointRounding.cs12
-rw-r--r--src/mscorlib/shared/System/MissingMethodException.cs60
-rw-r--r--src/mscorlib/shared/System/MulticastNotSupportedException.cs37
-rw-r--r--src/mscorlib/shared/System/NotFiniteNumberException.cs71
-rw-r--r--src/mscorlib/shared/System/NotImplementedException.cs40
-rw-r--r--src/mscorlib/shared/System/NotSupportedException.cs41
-rw-r--r--src/mscorlib/shared/System/NullReferenceException.cs41
-rw-r--r--src/mscorlib/shared/System/ObjectDisposedException.cs82
-rw-r--r--src/mscorlib/shared/System/ObsoleteAttribute.cs61
-rw-r--r--src/mscorlib/shared/System/OverflowException.cs41
-rw-r--r--src/mscorlib/shared/System/ParamArrayAttribute.cs21
-rw-r--r--src/mscorlib/shared/System/ParamsArray.cs82
-rw-r--r--src/mscorlib/shared/System/PlatformNotSupportedException.cs41
-rw-r--r--src/mscorlib/shared/System/Progress.cs105
-rw-r--r--src/mscorlib/shared/System/Random.cs274
-rw-r--r--src/mscorlib/shared/System/RankException.cs42
-rw-r--r--src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs35
-rw-r--r--src/mscorlib/shared/System/Reflection/Assembly.cs200
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyAlgorithmIdAttribute.cs27
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyCompanyAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyConfigurationAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyContentType.cs13
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyCopyrightAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyCultureAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyDefaultAliasAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyDelaySignAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyDescriptionAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyFileVersionAttribute.cs20
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyFlagsAttribute.cs43
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyInformationalVersionAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyKeyFileAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyKeyNameAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyMetadataAttribute.cs21
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyNameFlags.cs21
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyProductAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblySignatureKeyAttribute.cs21
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyTitleAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyTrademarkAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/AssemblyVersionAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/Binder.cs19
-rw-r--r--src/mscorlib/shared/System/Reflection/BindingFlags.cs50
-rw-r--r--src/mscorlib/shared/System/Reflection/CallingConventions.cs20
-rw-r--r--src/mscorlib/shared/System/Reflection/ConstructorInfo.cs40
-rw-r--r--src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs33
-rw-r--r--src/mscorlib/shared/System/Reflection/DefaultMemberAttribute.cs22
-rw-r--r--src/mscorlib/shared/System/Reflection/EventAttributes.cs22
-rw-r--r--src/mscorlib/shared/System/Reflection/EventInfo.cs115
-rw-r--r--src/mscorlib/shared/System/Reflection/ExceptionHandlingClauseOptions.cs16
-rw-r--r--src/mscorlib/shared/System/Reflection/FieldAttributes.cs40
-rw-r--r--src/mscorlib/shared/System/Reflection/FieldInfo.cs72
-rw-r--r--src/mscorlib/shared/System/Reflection/GenericParameterAttributes.cs20
-rw-r--r--src/mscorlib/shared/System/Reflection/ICustomAttributeProvider.cs13
-rw-r--r--src/mscorlib/shared/System/Reflection/IReflect.cs76
-rw-r--r--src/mscorlib/shared/System/Reflection/IReflectableType.cs12
-rw-r--r--src/mscorlib/shared/System/Reflection/ImageFileMachine.cs15
-rw-r--r--src/mscorlib/shared/System/Reflection/InterfaceMapping.cs14
-rw-r--r--src/mscorlib/shared/System/Reflection/IntrospectionExtensions.cs20
-rw-r--r--src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs33
-rw-r--r--src/mscorlib/shared/System/Reflection/ManifestResourceInfo.cs23
-rw-r--r--src/mscorlib/shared/System/Reflection/MemberFilter.cs8
-rw-r--r--src/mscorlib/shared/System/Reflection/MemberInfo.cs75
-rw-r--r--src/mscorlib/shared/System/Reflection/MemberInfoSerializationHolder.cs315
-rw-r--r--src/mscorlib/shared/System/Reflection/MemberTypes.cs20
-rw-r--r--src/mscorlib/shared/System/Reflection/MethodAttributes.cs50
-rw-r--r--src/mscorlib/shared/System/Reflection/MethodBase.cs86
-rw-r--r--src/mscorlib/shared/System/Reflection/MethodImplAttributes.cs37
-rw-r--r--src/mscorlib/shared/System/Reflection/MethodInfo.cs43
-rw-r--r--src/mscorlib/shared/System/Reflection/Missing.cs24
-rw-r--r--src/mscorlib/shared/System/Reflection/Module.cs182
-rw-r--r--src/mscorlib/shared/System/Reflection/ModuleResolveEventHandler.cs9
-rw-r--r--src/mscorlib/shared/System/Reflection/ObfuscateAssemblyAttribute.cs19
-rw-r--r--src/mscorlib/shared/System/Reflection/ObfuscationAttribute.cs21
-rw-r--r--src/mscorlib/shared/System/Reflection/ParameterAttributes.cs29
-rw-r--r--src/mscorlib/shared/System/Reflection/ParameterInfo.cs110
-rw-r--r--src/mscorlib/shared/System/Reflection/ParameterModifier.cs36
-rw-r--r--src/mscorlib/shared/System/Reflection/Pointer.cs61
-rw-r--r--src/mscorlib/shared/System/Reflection/PortableExecutableKinds.cs18
-rw-r--r--src/mscorlib/shared/System/Reflection/ProcessorArchitecture.cs16
-rw-r--r--src/mscorlib/shared/System/Reflection/PropertyAttributes.cs25
-rw-r--r--src/mscorlib/shared/System/Reflection/PropertyInfo.cs74
-rw-r--r--src/mscorlib/shared/System/Reflection/ReflectionContext.cs24
-rw-r--r--src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs46
-rw-r--r--src/mscorlib/shared/System/Reflection/ResourceAttributes.cs14
-rw-r--r--src/mscorlib/shared/System/Reflection/ResourceLocation.cs15
-rw-r--r--src/mscorlib/shared/System/Reflection/StrongNameKeyPair.cs74
-rw-r--r--src/mscorlib/shared/System/Reflection/TargetException.cs33
-rw-r--r--src/mscorlib/shared/System/Reflection/TargetInvocationException.cs29
-rw-r--r--src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs35
-rw-r--r--src/mscorlib/shared/System/Reflection/TypeAttributes.cs63
-rw-r--r--src/mscorlib/shared/System/Reflection/TypeDelegator.cs124
-rw-r--r--src/mscorlib/shared/System/Reflection/TypeFilter.cs8
-rw-r--r--src/mscorlib/shared/System/Reflection/TypeInfo.cs84
-rw-r--r--src/mscorlib/shared/System/ResolveEventArgs.cs25
-rw-r--r--src/mscorlib/shared/System/ResolveEventHandler.cs10
-rw-r--r--src/mscorlib/shared/System/Resources/IResourceReader.cs30
-rw-r--r--src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs36
-rw-r--r--src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs63
-rw-r--r--src/mscorlib/shared/System/Resources/NeutralResourcesLanguageAttribute.cs33
-rw-r--r--src/mscorlib/shared/System/Resources/ResourceTypeCode.cs58
-rw-r--r--src/mscorlib/shared/System/Resources/SatelliteContractVersionAttribute.cs31
-rw-r--r--src/mscorlib/shared/System/Resources/UltimateResourceFallbackLocation.cs25
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs17
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs16
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/CallerFilePathAttribute.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/CompilationRelaxations.cs16
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/CompilationRelaxationsAttribute.cs23
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs13
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs16
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/DefaultDependencyAttribute.cs18
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/DependencyAttribute.cs20
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs13
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/DiscardableAttribute.cs13
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/ExtensionAttribute.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs13
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/FixedBufferAttribute.cs32
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/FormattableStringFactory.cs58
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/IAsyncStateMachine.cs27
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/INotifyCompletion.cs39
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/ITuple.cs22
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/IndexerNameAttribute.cs15
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs21
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/IsConst.cs10
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/IsVolatile.cs12
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs16
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/LoadHint.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/MethodCodeType.cs17
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplOptions.cs21
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/ReadOnlyAttribute.cs21
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs33
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs31
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs19
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/SpecialNameAttribute.cs12
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/StateMachineAttribute.cs20
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/StringFreezingAttribute.cs15
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/StrongBox.cs59
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs13
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/TupleElementNamesAttribute.cs57
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs17
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs17
-rw-r--r--src/mscorlib/shared/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs12
-rw-r--r--src/mscorlib/shared/System/Runtime/ConstrainedExecution/Cer.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/ConstrainedExecution/Consistency.cs15
-rw-r--r--src/mscorlib/shared/System/Runtime/ConstrainedExecution/ReliabilityContractAttribute.cs33
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/CallingConvention.cs16
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/CharSet.cs21
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/ComVisibleAttribute.cs17
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs89
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/LayoutKind.cs14
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/StringBuffer.cs301
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs27
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedType.cs48
-rw-r--r--src/mscorlib/shared/System/Runtime/InteropServices/VarEnum.cs54
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/IDeserializationCallback.cs11
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/IFormatterConverter.cs28
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/IObjectReference.cs11
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/ISafeSerializationData.cs207
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/ISerializable.cs11
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/OnDeserializedAttribute.cs11
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/OnDeserializingAttribute.cs11
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/OnSerializedAttribute.cs11
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/OnSerializingAttribute.cs11
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/OptionalFieldAttribute.cs25
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/SafeSerializationEventArgs.cs31
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs39
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/SerializationInfoEnumerator.cs127
-rw-r--r--src/mscorlib/shared/System/Runtime/Serialization/StreamingContext.cs53
-rw-r--r--src/mscorlib/shared/System/Runtime/Versioning/NonVersionableAttribute.cs36
-rw-r--r--src/mscorlib/shared/System/Runtime/Versioning/TargetFrameworkAttribute.cs48
-rw-r--r--src/mscorlib/shared/System/Security/AllowPartiallyTrustedCallersAttribute.cs19
-rw-r--r--src/mscorlib/shared/System/Security/CryptographicException.cs44
-rw-r--r--src/mscorlib/shared/System/Security/PartialTrustVisibilityLevel.cs13
-rw-r--r--src/mscorlib/shared/System/Security/SafeBSTRHandle.cs81
-rw-r--r--src/mscorlib/shared/System/Security/SecureString.Unix.cs295
-rw-r--r--src/mscorlib/shared/System/Security/SecureString.Windows.cs311
-rw-r--r--src/mscorlib/shared/System/Security/SecureString.cs189
-rw-r--r--src/mscorlib/shared/System/Security/SecurityCriticalAttribute.cs36
-rw-r--r--src/mscorlib/shared/System/Security/SecurityCriticalScope.cs14
-rw-r--r--src/mscorlib/shared/System/Security/SecurityException.cs66
-rw-r--r--src/mscorlib/shared/System/Security/SecurityRuleSet.cs14
-rw-r--r--src/mscorlib/shared/System/Security/SecurityRulesAttribute.cs28
-rw-r--r--src/mscorlib/shared/System/Security/SecuritySafeCriticalAttribute.cs30
-rw-r--r--src/mscorlib/shared/System/Security/SecurityTransparentAttribute.cs19
-rw-r--r--src/mscorlib/shared/System/Security/SecurityTreatAsSafeAttribute.cs32
-rw-r--r--src/mscorlib/shared/System/Security/SuppressUnmanagedCodeSecurityAttribute.cs16
-rw-r--r--src/mscorlib/shared/System/Security/UnverifiableCodeAttribute.cs15
-rw-r--r--src/mscorlib/shared/System/Security/VerificationException.cs35
-rw-r--r--src/mscorlib/shared/System/StackOverflowException.cs41
-rw-r--r--src/mscorlib/shared/System/StringComparer.cs274
-rw-r--r--src/mscorlib/shared/System/StringComparison.cs17
-rw-r--r--src/mscorlib/shared/System/StringSplitOptions.cs13
-rw-r--r--src/mscorlib/shared/System/SystemException.cs32
-rw-r--r--src/mscorlib/shared/System/Text/ASCIIEncoding.cs973
-rw-r--r--src/mscorlib/shared/System/Text/Decoder.cs339
-rw-r--r--src/mscorlib/shared/System/Text/Encoder.cs333
-rw-r--r--src/mscorlib/shared/System/Text/EncodingInfo.cs72
-rw-r--r--src/mscorlib/shared/System/Text/EncodingNLS.cs322
-rw-r--r--src/mscorlib/shared/System/Text/EncodingProvider.cs136
-rw-r--r--src/mscorlib/shared/System/Text/Normalization.cs29
-rw-r--r--src/mscorlib/shared/System/Text/StringBuilder.cs2409
-rw-r--r--src/mscorlib/shared/System/Text/UTF32Encoding.cs1234
-rw-r--r--src/mscorlib/shared/System/Text/UTF8Encoding.cs2668
-rw-r--r--src/mscorlib/shared/System/Text/UnicodeEncoding.cs2058
-rw-r--r--src/mscorlib/shared/System/ThreadAttributes.cs28
-rw-r--r--src/mscorlib/shared/System/ThreadStaticAttribute.cs28
-rw-r--r--src/mscorlib/shared/System/Threading/AbandonedMutexException.cs77
-rw-r--r--src/mscorlib/shared/System/Threading/ApartmentState.cs16
-rw-r--r--src/mscorlib/shared/System/Threading/AsyncLocal.cs484
-rw-r--r--src/mscorlib/shared/System/Threading/AutoResetEvent.cs12
-rw-r--r--src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs116
-rw-r--r--src/mscorlib/shared/System/Threading/EventResetMode.cs22
-rw-r--r--src/mscorlib/shared/System/Threading/ExecutionContext.cs370
-rw-r--r--src/mscorlib/shared/System/Threading/LazyThreadSafetyMode.cs44
-rw-r--r--src/mscorlib/shared/System/Threading/LockRecursionException.cs29
-rw-r--r--src/mscorlib/shared/System/Threading/ManualResetEvent.cs12
-rw-r--r--src/mscorlib/shared/System/Threading/ParameterizedThreadStart.cs18
-rw-r--r--src/mscorlib/shared/System/Threading/SemaphoreFullException.cs29
-rw-r--r--src/mscorlib/shared/System/Threading/SendOrPostCallback.cs8
-rw-r--r--src/mscorlib/shared/System/Threading/SynchronizationLockException.cs44
-rw-r--r--src/mscorlib/shared/System/Threading/Tasks/TaskCanceledException.cs89
-rw-r--r--src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs48
-rw-r--r--src/mscorlib/shared/System/Threading/Tasks/TaskSchedulerException.cs77
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadAbortException.cs36
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadPriority.cs18
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadStart.cs18
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadStartException.cs29
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadState.cs24
-rw-r--r--src/mscorlib/shared/System/Threading/ThreadStateException.cs45
-rw-r--r--src/mscorlib/shared/System/Threading/Timeout.cs20
-rw-r--r--src/mscorlib/shared/System/Threading/TimeoutHelper.cs54
-rw-r--r--src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs31
-rw-r--r--src/mscorlib/shared/System/TimeZone.cs281
-rw-r--r--src/mscorlib/shared/System/TimeZoneNotFoundException.cs28
-rw-r--r--src/mscorlib/shared/System/TimeoutException.cs41
-rw-r--r--src/mscorlib/shared/System/TupleExtensions.cs930
-rw-r--r--src/mscorlib/shared/System/Type.Enum.cs186
-rw-r--r--src/mscorlib/shared/System/Type.Helpers.cs527
-rw-r--r--src/mscorlib/shared/System/Type.cs358
-rw-r--r--src/mscorlib/shared/System/TypeAccessException.cs34
-rw-r--r--src/mscorlib/shared/System/TypeCode.cs48
-rw-r--r--src/mscorlib/shared/System/TypeInitializationException.cs79
-rw-r--r--src/mscorlib/shared/System/TypeUnloadedException.cs39
-rw-r--r--src/mscorlib/shared/System/UnauthorizedAccessException.cs45
-rw-r--r--src/mscorlib/shared/System/UnhandledExceptionEventArgs.cs29
-rw-r--r--src/mscorlib/shared/System/UnhandledExceptionEventHandler.cs9
-rw-r--r--src/mscorlib/shared/System/UnitySerializationHolder.cs329
-rw-r--r--src/mscorlib/shared/System/ValueTuple.cs2324
-rw-r--r--src/mscorlib/shared/System/Version.cs495
-rw-r--r--src/mscorlib/shared/System/Void.cs17
441 files changed, 86190 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/Action.cs b/src/mscorlib/shared/System/Action.cs
new file mode 100644
index 0000000000..b82c14d9dc
--- /dev/null
+++ b/src/mscorlib/shared/System/Action.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ public delegate void Action<in T>(T obj);
+
+ public delegate void Action();
+ public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
+ public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3);
+ public delegate void Action<in T1, in T2, in T3, in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+
+ public delegate TResult Func<out TResult>();
+ public delegate TResult Func<in T, out TResult>(T arg);
+ public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
+ public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
+ public delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
+
+ public delegate void Action<in T1, in T2, in T3, in T4, in T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
+ public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
+ public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
+ public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
+
+ public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
+ public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
+ public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
+ public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
+
+ public delegate int Comparison<in T>(T x, T y);
+
+ public delegate TOutput Converter<in TInput, out TOutput>(TInput input);
+
+ public delegate bool Predicate<in T>(T obj);
+}
diff --git a/src/mscorlib/shared/System/ApplicationException.cs b/src/mscorlib/shared/System/ApplicationException.cs
new file mode 100644
index 0000000000..900feb57f9
--- /dev/null
+++ b/src/mscorlib/shared/System/ApplicationException.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: The base class for all "less serious" exceptions that must be
+** declared or caught.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ // The ApplicationException is the base class for nonfatal,
+ // application errors that occur. These exceptions are generated
+ // (i.e., thrown) by an application, not the Runtime. Applications that need
+ // to create their own exceptions do so by extending this class.
+ // ApplicationException extends but adds no new functionality to
+ // RecoverableException.
+ //
+ [Serializable]
+ public class ApplicationException : Exception
+ {
+ // Creates a new ApplicationException with its message string set to
+ // the empty string, its HRESULT set to COR_E_APPLICATION,
+ // and its ExceptionInfo reference set to null.
+ public ApplicationException()
+ : base(SR.Arg_ApplicationException)
+ {
+ HResult = __HResults.COR_E_APPLICATION;
+ }
+
+ // Creates a new ApplicationException with its message string set to
+ // message, its HRESULT set to COR_E_APPLICATION,
+ // and its ExceptionInfo reference set to null.
+ //
+ public ApplicationException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_APPLICATION;
+ }
+
+ public ApplicationException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_APPLICATION;
+ }
+
+ protected ApplicationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/ArgumentException.cs b/src/mscorlib/shared/System/ArgumentException.cs
new file mode 100644
index 0000000000..96afbe10d9
--- /dev/null
+++ b/src/mscorlib/shared/System/ArgumentException.cs
@@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for invalid arguments to a method.
+**
+**
+=============================================================================*/
+
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace System
+{
+ // The ArgumentException is thrown when an argument does not meet
+ // the contract of the method. Ideally it should give a meaningful error
+ // message describing what was wrong and which parameter is incorrect.
+ //
+ [Serializable]
+ public class ArgumentException : SystemException
+ {
+ private String _paramName;
+
+ // Creates a new ArgumentException with its message
+ // string set to the empty string.
+ public ArgumentException()
+ : base(SR.Arg_ArgumentException)
+ {
+ HResult = __HResults.COR_E_ARGUMENT;
+ }
+
+ // Creates a new ArgumentException with its message
+ // string set to message.
+ //
+ public ArgumentException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_ARGUMENT;
+ }
+
+ public ArgumentException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_ARGUMENT;
+ }
+
+ public ArgumentException(String message, String paramName, Exception innerException)
+ : base(message, innerException)
+ {
+ _paramName = paramName;
+ HResult = __HResults.COR_E_ARGUMENT;
+ }
+
+ public ArgumentException(String message, String paramName)
+ : base(message)
+ {
+ _paramName = paramName;
+ HResult = __HResults.COR_E_ARGUMENT;
+ }
+
+ protected ArgumentException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ _paramName = info.GetString("ParamName");
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData(info, context);
+ info.AddValue("ParamName", _paramName, typeof(String));
+ }
+
+ public override String Message
+ {
+ get
+ {
+ String s = base.Message;
+ if (!String.IsNullOrEmpty(_paramName))
+ {
+ String resourceString = SR.Format(SR.Arg_ParamName_Name, _paramName);
+ return s + Environment.NewLine + resourceString;
+ }
+ else
+ return s;
+ }
+ }
+
+ public virtual String ParamName
+ {
+ get { return _paramName; }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/ArgumentNullException.cs b/src/mscorlib/shared/System/ArgumentNullException.cs
new file mode 100644
index 0000000000..3a86223ccf
--- /dev/null
+++ b/src/mscorlib/shared/System/ArgumentNullException.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for null arguments to a method.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ // The ArgumentException is thrown when an argument
+ // is null when it shouldn't be.
+ //
+ [Serializable]
+ public class ArgumentNullException : ArgumentException
+ {
+ // Creates a new ArgumentNullException with its message
+ // string set to a default message explaining an argument was null.
+ public ArgumentNullException()
+ : base(SR.ArgumentNull_Generic)
+ {
+ // Use E_POINTER - COM used that for null pointers. Description is "invalid pointer"
+ HResult = __HResults.E_POINTER;
+ }
+
+ public ArgumentNullException(String paramName)
+ : base(SR.ArgumentNull_Generic, paramName)
+ {
+ HResult = __HResults.E_POINTER;
+ }
+
+ public ArgumentNullException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.E_POINTER;
+ }
+
+ public ArgumentNullException(String paramName, String message)
+ : base(message, paramName)
+ {
+ HResult = __HResults.E_POINTER;
+ }
+
+ protected ArgumentNullException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/ArithmeticException.cs b/src/mscorlib/shared/System/ArithmeticException.cs
new file mode 100644
index 0000000000..081ba454f5
--- /dev/null
+++ b/src/mscorlib/shared/System/ArithmeticException.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for bad arithmetic conditions!
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ // The ArithmeticException is thrown when overflow or underflow
+ // occurs.
+ //
+ [Serializable]
+ public class ArithmeticException : SystemException
+ {
+ // Creates a new ArithmeticException with its message string set to
+ // the empty string, its HRESULT set to COR_E_ARITHMETIC,
+ // and its ExceptionInfo reference set to null.
+ public ArithmeticException()
+ : base(SR.Arg_ArithmeticException)
+ {
+ HResult = __HResults.COR_E_ARITHMETIC;
+ }
+
+ // Creates a new ArithmeticException with its message string set to
+ // message, its HRESULT set to COR_E_ARITHMETIC,
+ // and its ExceptionInfo reference set to null.
+ //
+ public ArithmeticException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_ARITHMETIC;
+ }
+
+ public ArithmeticException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_ARITHMETIC;
+ }
+
+ protected ArithmeticException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/ArrayTypeMismatchException.cs b/src/mscorlib/shared/System/ArrayTypeMismatchException.cs
new file mode 100644
index 0000000000..3e941fdf8e
--- /dev/null
+++ b/src/mscorlib/shared/System/ArrayTypeMismatchException.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: The arrays are of different primitive types.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ // The ArrayMismatchException is thrown when an attempt to store
+ // an object of the wrong type within an array occurs.
+ //
+ [Serializable]
+ public class ArrayTypeMismatchException : SystemException
+ {
+ // Creates a new ArrayMismatchException with its message string set to
+ // the empty string, its HRESULT set to COR_E_ARRAYTYPEMISMATCH,
+ // and its ExceptionInfo reference set to null.
+ public ArrayTypeMismatchException()
+ : base(SR.Arg_ArrayTypeMismatchException)
+ {
+ HResult = __HResults.COR_E_ARRAYTYPEMISMATCH;
+ }
+
+ // Creates a new ArrayMismatchException with its message string set to
+ // message, its HRESULT set to COR_E_ARRAYTYPEMISMATCH,
+ // and its ExceptionInfo reference set to null.
+ //
+ public ArrayTypeMismatchException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_ARRAYTYPEMISMATCH;
+ }
+
+ public ArrayTypeMismatchException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_ARRAYTYPEMISMATCH;
+ }
+
+ protected ArrayTypeMismatchException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/AssemblyLoadEventArgs.cs b/src/mscorlib/shared/System/AssemblyLoadEventArgs.cs
new file mode 100644
index 0000000000..d7e5249693
--- /dev/null
+++ b/src/mscorlib/shared/System/AssemblyLoadEventArgs.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Reflection;
+
+namespace System
+{
+ public class AssemblyLoadEventArgs : EventArgs
+ {
+ public AssemblyLoadEventArgs(Assembly loadedAssembly)
+ {
+ LoadedAssembly = loadedAssembly;
+ }
+
+ public Assembly LoadedAssembly { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/AssemblyLoadEventHandler.cs b/src/mscorlib/shared/System/AssemblyLoadEventHandler.cs
new file mode 100644
index 0000000000..752379fdc7
--- /dev/null
+++ b/src/mscorlib/shared/System/AssemblyLoadEventHandler.cs
@@ -0,0 +1,8 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ public delegate void AssemblyLoadEventHandler(object sender, AssemblyLoadEventArgs args);
+}
diff --git a/src/mscorlib/shared/System/AsyncCallback.cs b/src/mscorlib/shared/System/AsyncCallback.cs
new file mode 100644
index 0000000000..5c49535cff
--- /dev/null
+++ b/src/mscorlib/shared/System/AsyncCallback.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: AsyncCallbackDelegate
+**
+** Purpose: Type of callback for async operations
+**
+===========================================================*/
+
+namespace System
+{
+ [Serializable]
+ public delegate void AsyncCallback(IAsyncResult ar);
+}
diff --git a/src/mscorlib/shared/System/AttributeTargets.cs b/src/mscorlib/shared/System/AttributeTargets.cs
new file mode 100644
index 0000000000..fdfa4ab730
--- /dev/null
+++ b/src/mscorlib/shared/System/AttributeTargets.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+namespace System
+{
+ // Enum used to indicate all the elements of the
+ // VOS it is valid to attach this element to.
+ [Flags]
+ [Serializable]
+ public enum AttributeTargets
+ {
+ Assembly = 0x0001,
+ Module = 0x0002,
+ Class = 0x0004,
+ Struct = 0x0008,
+ Enum = 0x0010,
+ Constructor = 0x0020,
+ Method = 0x0040,
+ Property = 0x0080,
+ Field = 0x0100,
+ Event = 0x0200,
+ Interface = 0x0400,
+ Parameter = 0x0800,
+ Delegate = 0x1000,
+ ReturnValue = 0x2000,
+ GenericParameter = 0x4000,
+
+ All = Assembly | Module | Class | Struct | Enum | Constructor |
+ Method | Property | Field | Event | Interface | Parameter |
+ Delegate | ReturnValue | GenericParameter
+ }
+}
diff --git a/src/mscorlib/shared/System/AttributeUsageAttribute.cs b/src/mscorlib/shared/System/AttributeUsageAttribute.cs
new file mode 100644
index 0000000000..6f9aeb20f3
--- /dev/null
+++ b/src/mscorlib/shared/System/AttributeUsageAttribute.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: The class denotes how to specify the usage of an attribute
+**
+**
+===========================================================*/
+
+using System.Reflection;
+
+namespace System
+{
+ /* By default, attributes are inherited and multiple attributes are not allowed */
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Class, Inherited = true)]
+ public sealed class AttributeUsageAttribute : Attribute
+ {
+ private AttributeTargets _attributeTarget = AttributeTargets.All; // Defaults to all
+ private bool _allowMultiple = false; // Defaults to false
+ private bool _inherited = true; // Defaults to true
+
+ internal static AttributeUsageAttribute Default = new AttributeUsageAttribute(AttributeTargets.All);
+
+ //Constructors
+ public AttributeUsageAttribute(AttributeTargets validOn)
+ {
+ _attributeTarget = validOn;
+ }
+ internal AttributeUsageAttribute(AttributeTargets validOn, bool allowMultiple, bool inherited)
+ {
+ _attributeTarget = validOn;
+ _allowMultiple = allowMultiple;
+ _inherited = inherited;
+ }
+
+ public AttributeTargets ValidOn
+ {
+ get { return _attributeTarget; }
+ }
+
+ public bool AllowMultiple
+ {
+ get { return _allowMultiple; }
+ set { _allowMultiple = value; }
+ }
+
+ public bool Inherited
+ {
+ get { return _inherited; }
+ set { _inherited = value; }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Buffers/ArrayPool.cs b/src/mscorlib/shared/System/Buffers/ArrayPool.cs
new file mode 100644
index 0000000000..77a07f7fa5
--- /dev/null
+++ b/src/mscorlib/shared/System/Buffers/ArrayPool.cs
@@ -0,0 +1,100 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Buffers
+{
+ /// <summary>
+ /// Provides a resource pool that enables reusing instances of type <see cref="T:T[]"/>.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// Renting and returning buffers with an <see cref="ArrayPool{T}"/> can increase performance
+ /// in situations where arrays are created and destroyed frequently, resulting in significant
+ /// memory pressure on the garbage collector.
+ /// </para>
+ /// <para>
+ /// This class is thread-safe. All members may be used by multiple threads concurrently.
+ /// </para>
+ /// </remarks>
+ public abstract class ArrayPool<T>
+ {
+ /// <summary>
+ /// Retrieves a shared <see cref="ArrayPool{T}"/> instance.
+ /// </summary>
+ /// <remarks>
+ /// The shared pool provides a default implementation of <see cref="ArrayPool{T}"/>
+ /// that's intended for general applicability. It maintains arrays of multiple sizes, and
+ /// may hand back a larger array than was actually requested, but will never hand back a smaller
+ /// array than was requested. Renting a buffer from it with <see cref="Rent"/> will result in an
+ /// existing buffer being taken from the pool if an appropriate buffer is available or in a new
+ /// buffer being allocated if one is not available.
+ /// byte[] and char[] are the most commonly pooled array types. For these we use a special pool type
+ /// optimized for very fast access speeds, at the expense of more memory consumption.
+ /// The shared pool instance is created lazily on first access.
+ /// </remarks>
+ public static ArrayPool<T> Shared { get; } =
+ typeof(T) == typeof(byte) || typeof(T) == typeof(char) ? new TlsOverPerCoreLockedStacksArrayPool<T>() :
+ Create();
+
+ /// <summary>
+ /// Creates a new <see cref="ArrayPool{T}"/> instance using default configuration options.
+ /// </summary>
+ /// <returns>A new <see cref="ArrayPool{T}"/> instance.</returns>
+ public static ArrayPool<T> Create() => new ConfigurableArrayPool<T>();
+
+ /// <summary>
+ /// Creates a new <see cref="ArrayPool{T}"/> instance using custom configuration options.
+ /// </summary>
+ /// <param name="maxArrayLength">The maximum length of array instances that may be stored in the pool.</param>
+ /// <param name="maxArraysPerBucket">
+ /// The maximum number of array instances that may be stored in each bucket in the pool. The pool
+ /// groups arrays of similar lengths into buckets for faster access.
+ /// </param>
+ /// <returns>A new <see cref="ArrayPool{T}"/> instance with the specified configuration options.</returns>
+ /// <remarks>
+ /// The created pool will group arrays into buckets, with no more than <paramref name="maxArraysPerBucket"/>
+ /// in each bucket and with those arrays not exceeding <paramref name="maxArrayLength"/> in length.
+ /// </remarks>
+ public static ArrayPool<T> Create(int maxArrayLength, int maxArraysPerBucket) =>
+ new ConfigurableArrayPool<T>(maxArrayLength, maxArraysPerBucket);
+
+ /// <summary>
+ /// Retrieves a buffer that is at least the requested length.
+ /// </summary>
+ /// <param name="minimumLength">The minimum length of the array needed.</param>
+ /// <returns>
+ /// An <see cref="T:T[]"/> that is at least <paramref name="minimumLength"/> in length.
+ /// </returns>
+ /// <remarks>
+ /// This buffer is loaned to the caller and should be returned to the same pool via
+ /// <see cref="Return"/> so that it may be reused in subsequent usage of <see cref="Rent"/>.
+ /// It is not a fatal error to not return a rented buffer, but failure to do so may lead to
+ /// decreased application performance, as the pool may need to create a new buffer to replace
+ /// the one lost.
+ /// </remarks>
+ public abstract T[] Rent(int minimumLength);
+
+ /// <summary>
+ /// Returns to the pool an array that was previously obtained via <see cref="Rent"/> on the same
+ /// <see cref="ArrayPool{T}"/> instance.
+ /// </summary>
+ /// <param name="array">
+ /// The buffer previously obtained from <see cref="Rent"/> to return to the pool.
+ /// </param>
+ /// <param name="clearArray">
+ /// If <c>true</c> and if the pool will store the buffer to enable subsequent reuse, <see cref="Return"/>
+ /// will clear <paramref name="array"/> of its contents so that a subsequent consumer via <see cref="Rent"/>
+ /// will not see the previous consumer's content. If <c>false</c> or if the pool will release the buffer,
+ /// the array's contents are left unchanged.
+ /// </param>
+ /// <remarks>
+ /// Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer
+ /// and must not use it. The reference returned from a given call to <see cref="Rent"/> must only be
+ /// returned via <see cref="Return"/> once. The default <see cref="ArrayPool{T}"/>
+ /// may hold onto the returned buffer in order to rent it again, or it may release the returned buffer
+ /// if it's determined that the pool already has enough buffers stored.
+ /// </remarks>
+ public abstract void Return(T[] array, bool clearArray = false);
+ }
+}
diff --git a/src/mscorlib/shared/System/Buffers/ConfigurableArrayPool.cs b/src/mscorlib/shared/System/Buffers/ConfigurableArrayPool.cs
new file mode 100644
index 0000000000..f7b6034d20
--- /dev/null
+++ b/src/mscorlib/shared/System/Buffers/ConfigurableArrayPool.cs
@@ -0,0 +1,265 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Threading;
+
+namespace System.Buffers
+{
+ internal sealed partial class ConfigurableArrayPool<T> : ArrayPool<T>
+ {
+ /// <summary>The default maximum length of each array in the pool (2^20).</summary>
+ private const int DefaultMaxArrayLength = 1024 * 1024;
+ /// <summary>The default maximum number of arrays per bucket that are available for rent.</summary>
+ private const int DefaultMaxNumberOfArraysPerBucket = 50;
+
+ private readonly Bucket[] _buckets;
+
+ internal ConfigurableArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket)
+ {
+ }
+
+ internal ConfigurableArrayPool(int maxArrayLength, int maxArraysPerBucket)
+ {
+ if (maxArrayLength <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(maxArrayLength));
+ }
+ if (maxArraysPerBucket <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(maxArraysPerBucket));
+ }
+
+ // Our bucketing algorithm has a min length of 2^4 and a max length of 2^30.
+ // Constrain the actual max used to those values.
+ const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000;
+ if (maxArrayLength > MaximumArrayLength)
+ {
+ maxArrayLength = MaximumArrayLength;
+ }
+ else if (maxArrayLength < MinimumArrayLength)
+ {
+ maxArrayLength = MinimumArrayLength;
+ }
+
+ // Create the buckets.
+ int poolId = Id;
+ int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength);
+ var buckets = new Bucket[maxBuckets + 1];
+ for (int i = 0; i < buckets.Length; i++)
+ {
+ buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId);
+ }
+ _buckets = buckets;
+ }
+
+ /// <summary>Gets an ID for the pool to use with events.</summary>
+ private int Id => GetHashCode();
+
+ public override T[] Rent(int minimumLength)
+ {
+ // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though
+ // pooling such an array isn't valuable) as it's a valid length array, and we want the pool
+ // to be usable in general instead of using `new`, even for computed lengths.
+ if (minimumLength < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(minimumLength));
+ }
+ else if (minimumLength == 0)
+ {
+ // No need for events with the empty array. Our pool is effectively infinite
+ // and we'll never allocate for rents and never store for returns.
+ return Array.Empty<T>();
+ }
+
+ var log = ArrayPoolEventSource.Log;
+ T[] buffer = null;
+
+ int index = Utilities.SelectBucketIndex(minimumLength);
+ if (index < _buckets.Length)
+ {
+ // Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the
+ // next higher bucket and try that one, but only try at most a few buckets.
+ const int MaxBucketsToTry = 2;
+ int i = index;
+ do
+ {
+ // Attempt to rent from the bucket. If we get a buffer from it, return it.
+ buffer = _buckets[i].Rent();
+ if (buffer != null)
+ {
+ if (log.IsEnabled())
+ {
+ log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, _buckets[i].Id);
+ }
+ return buffer;
+ }
+ }
+ while (++i < _buckets.Length && i != index + MaxBucketsToTry);
+
+ // The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding
+ // to the appropriate bucket.
+ buffer = new T[_buckets[index]._bufferLength];
+ }
+ else
+ {
+ // The request was for a size too large for the pool. Allocate an array of exactly the requested length.
+ // When it's returned to the pool, we'll simply throw it away.
+ buffer = new T[minimumLength];
+ }
+
+ if (log.IsEnabled())
+ {
+ int bufferId = buffer.GetHashCode(), bucketId = -1; // no bucket for an on-demand allocated buffer
+ log.BufferRented(bufferId, buffer.Length, Id, bucketId);
+ log.BufferAllocated(bufferId, buffer.Length, Id, bucketId, index >= _buckets.Length ?
+ ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize : ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted);
+ }
+
+ return buffer;
+ }
+
+ public override void Return(T[] array, bool clearArray = false)
+ {
+ if (array == null)
+ {
+ throw new ArgumentNullException(nameof(array));
+ }
+ else if (array.Length == 0)
+ {
+ // Ignore empty arrays. When a zero-length array is rented, we return a singleton
+ // rather than actually taking a buffer out of the lowest bucket.
+ return;
+ }
+
+ // Determine with what bucket this array length is associated
+ int bucket = Utilities.SelectBucketIndex(array.Length);
+
+ // If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool
+ if (bucket < _buckets.Length)
+ {
+ // Clear the array if the user requests
+ if (clearArray)
+ {
+ Array.Clear(array, 0, array.Length);
+ }
+
+ // Return the buffer to its bucket. In the future, we might consider having Return return false
+ // instead of dropping a bucket, in which case we could try to return to a lower-sized bucket,
+ // just as how in Rent we allow renting from a higher-sized bucket.
+ _buckets[bucket].Return(array);
+ }
+
+ // Log that the buffer was returned
+ var log = ArrayPoolEventSource.Log;
+ if (log.IsEnabled())
+ {
+ log.BufferReturned(array.GetHashCode(), array.Length, Id);
+ }
+ }
+
+ /// <summary>Provides a thread-safe bucket containing buffers that can be Rent'd and Return'd.</summary>
+ private sealed class Bucket
+ {
+ internal readonly int _bufferLength;
+ private readonly T[][] _buffers;
+ private readonly int _poolId;
+
+ private SpinLock _lock; // do not make this readonly; it's a mutable struct
+ private int _index;
+
+ /// <summary>
+ /// Creates the pool with numberOfBuffers arrays where each buffer is of bufferLength length.
+ /// </summary>
+ internal Bucket(int bufferLength, int numberOfBuffers, int poolId)
+ {
+ _lock = new SpinLock(Debugger.IsAttached); // only enable thread tracking if debugger is attached; it adds non-trivial overheads to Enter/Exit
+ _buffers = new T[numberOfBuffers][];
+ _bufferLength = bufferLength;
+ _poolId = poolId;
+ }
+
+ /// <summary>Gets an ID for the bucket to use with events.</summary>
+ internal int Id => GetHashCode();
+
+ /// <summary>Takes an array from the bucket. If the bucket is empty, returns null.</summary>
+ internal T[] Rent()
+ {
+ T[][] buffers = _buffers;
+ T[] buffer = null;
+
+ // While holding the lock, grab whatever is at the next available index and
+ // update the index. We do as little work as possible while holding the spin
+ // lock to minimize contention with other threads. The try/finally is
+ // necessary to properly handle thread aborts on platforms which have them.
+ bool lockTaken = false, allocateBuffer = false;
+ try
+ {
+ _lock.Enter(ref lockTaken);
+
+ if (_index < buffers.Length)
+ {
+ buffer = buffers[_index];
+ buffers[_index++] = null;
+ allocateBuffer = buffer == null;
+ }
+ }
+ finally
+ {
+ if (lockTaken) _lock.Exit(false);
+ }
+
+ // While we were holding the lock, we grabbed whatever was at the next available index, if
+ // there was one. If we tried and if we got back null, that means we hadn't yet allocated
+ // for that slot, in which case we should do so now.
+ if (allocateBuffer)
+ {
+ buffer = new T[_bufferLength];
+
+ var log = ArrayPoolEventSource.Log;
+ if (log.IsEnabled())
+ {
+ log.BufferAllocated(buffer.GetHashCode(), _bufferLength, _poolId, Id,
+ ArrayPoolEventSource.BufferAllocatedReason.Pooled);
+ }
+ }
+
+ return buffer;
+ }
+
+ /// <summary>
+ /// Attempts to return the buffer to the bucket. If successful, the buffer will be stored
+ /// in the bucket and true will be returned; otherwise, the buffer won't be stored, and false
+ /// will be returned.
+ /// </summary>
+ internal void Return(T[] array)
+ {
+ // Check to see if the buffer is the correct size for this bucket
+ if (array.Length != _bufferLength)
+ {
+ throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array));
+ }
+
+ // While holding the spin lock, if there's room available in the bucket,
+ // put the buffer into the next available slot. Otherwise, we just drop it.
+ // The try/finally is necessary to properly handle thread aborts on platforms
+ // which have them.
+ bool lockTaken = false;
+ try
+ {
+ _lock.Enter(ref lockTaken);
+
+ if (_index != 0)
+ {
+ _buffers[--_index] = array;
+ }
+ }
+ finally
+ {
+ if (lockTaken) _lock.Exit(false);
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs b/src/mscorlib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs
new file mode 100644
index 0000000000..64c5cebe85
--- /dev/null
+++ b/src/mscorlib/shared/System/Buffers/TlsOverPerCoreLockedStacksArrayPool.cs
@@ -0,0 +1,292 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32;
+using System.Runtime.CompilerServices;
+using System.Threading;
+
+namespace System.Buffers
+{
+ /// <summary>
+ /// Provides an ArrayPool implementation meant to be used as the singleton returned from ArrayPool.Shared.
+ /// </summary>
+ /// <remarks>
+ /// The implementation uses a tiered caching scheme, with a small per-thread cache for each array size, followed
+ /// by a cache per array size shared by all threads, split into per-core stacks meant to be used by threads
+ /// running on that core. Locks are used to protect each per-core stack, because a thread can migrate after
+ /// checking its processor number, because multiple threads could interleave on the same core, and because
+ /// a thread is allowed to check other core's buckets if its core's bucket is empty/full.
+ /// </remarks>
+ internal sealed partial class TlsOverPerCoreLockedStacksArrayPool<T> : ArrayPool<T>
+ {
+ // TODO: #7747: "Investigate optimizing ArrayPool heuristics"
+ // - Explore caching in TLS more than one array per size per thread, and moving stale buffers to the global queue.
+ // - Explore dumping stale buffers from the global queue, similar to PinnableBufferCache (maybe merging them).
+ // - Explore changing the size of each per-core bucket, potentially dynamically or based on other factors like array size.
+ // - Explore changing number of buckets and what sizes of arrays are cached.
+ // - Investigate whether false sharing is causing any issues, in particular on LockedStack's count and the contents of its array.
+ // ...
+
+ /// <summary>The number of buckets (array sizes) in the pool, one for each array length, starting from length 16.</summary>
+ private const int NumBuckets = 17; // Utilities.SelectBucketIndex(2*1024*1024)
+ /// <summary>Maximum number of per-core stacks to use per array size.</summary>
+ private const int MaxPerCorePerArraySizeStacks = 64; // selected to avoid needing to worry about processor groups
+ /// <summary>The maximum number of buffers to store in a bucket's global queue.</summary>
+ private const int MaxBuffersPerArraySizePerCore = 8;
+
+ /// <summary>The length of arrays stored in the corresponding indices in <see cref="_buckets"/> and <see cref="t_tlsBuckets"/>.</summary>
+ private readonly int[] _bucketArraySizes;
+ /// <summary>
+ /// An array of per-core array stacks. The slots are lazily initialized to avoid creating
+ /// lots of overhead for unused array sizes.
+ /// </summary>
+ private readonly PerCoreLockedStacks[] _buckets = new PerCoreLockedStacks[NumBuckets];
+ /// <summary>A per-thread array of arrays, to cache one array per array size per thread.</summary>
+ [ThreadStatic]
+ private static T[][] t_tlsBuckets;
+
+ /// <summary>Initialize the pool.</summary>
+ public TlsOverPerCoreLockedStacksArrayPool()
+ {
+ var sizes = new int[NumBuckets];
+ for (int i = 0; i < sizes.Length; i++)
+ {
+ sizes[i] = Utilities.GetMaxSizeForBucket(i);
+ }
+ _bucketArraySizes = sizes;
+ }
+
+ /// <summary>Allocate a new PerCoreLockedStacks and try to store it into the <see cref="_buckets"/> array.</summary>
+ private PerCoreLockedStacks CreatePerCoreLockedStacks(int bucketIndex)
+ {
+ var inst = new PerCoreLockedStacks();
+ return Interlocked.CompareExchange(ref _buckets[bucketIndex], inst, null) ?? inst;
+ }
+
+ /// <summary>Gets an ID for the pool to use with events.</summary>
+ private int Id => GetHashCode();
+
+ public override T[] Rent(int minimumLength)
+ {
+ // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though
+ // pooling such an array isn't valuable) as it's a valid length array, and we want the pool
+ // to be usable in general instead of using `new`, even for computed lengths.
+ if (minimumLength < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(minimumLength));
+ }
+ else if (minimumLength == 0)
+ {
+ // No need to log the empty array. Our pool is effectively infinite
+ // and we'll never allocate for rents and never store for returns.
+ return Array.Empty<T>();
+ }
+
+ ArrayPoolEventSource log = ArrayPoolEventSource.Log;
+ T[] buffer;
+
+ // Get the bucket number for the array length
+ int bucketIndex = Utilities.SelectBucketIndex(minimumLength);
+
+ // If the array could come from a bucket...
+ if (bucketIndex < _buckets.Length)
+ {
+ // First try to get it from TLS if possible.
+ T[][] tlsBuckets = t_tlsBuckets;
+ if (tlsBuckets != null)
+ {
+ buffer = tlsBuckets[bucketIndex];
+ if (buffer != null)
+ {
+ tlsBuckets[bucketIndex] = null;
+ if (log.IsEnabled())
+ {
+ log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex);
+ }
+ return buffer;
+ }
+ }
+
+ // We couldn't get a buffer from TLS, so try the global stack.
+ PerCoreLockedStacks b = _buckets[bucketIndex];
+ if (b != null)
+ {
+ buffer = b.TryPop();
+ if (buffer != null)
+ {
+ if (log.IsEnabled())
+ {
+ log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, bucketIndex);
+ }
+ return buffer;
+ }
+ }
+
+ // No buffer available. Allocate a new buffer with a size corresponding to the appropriate bucket.
+ buffer = new T[_bucketArraySizes[bucketIndex]];
+ }
+ else
+ {
+ // The request was for a size too large for the pool. Allocate an array of exactly the requested length.
+ // When it's returned to the pool, we'll simply throw it away.
+ buffer = new T[minimumLength];
+ }
+
+ if (log.IsEnabled())
+ {
+ int bufferId = buffer.GetHashCode(), bucketId = -1; // no bucket for an on-demand allocated buffer
+ log.BufferRented(bufferId, buffer.Length, Id, bucketId);
+ log.BufferAllocated(bufferId, buffer.Length, Id, bucketId, bucketIndex >= _buckets.Length ?
+ ArrayPoolEventSource.BufferAllocatedReason.OverMaximumSize :
+ ArrayPoolEventSource.BufferAllocatedReason.PoolExhausted);
+ }
+
+ return buffer;
+ }
+
+ public override void Return(T[] array, bool clearArray = false)
+ {
+ if (array == null)
+ {
+ throw new ArgumentNullException(nameof(array));
+ }
+
+ // Determine with what bucket this array length is associated
+ int bucketIndex = Utilities.SelectBucketIndex(array.Length);
+
+ // If we can tell that the buffer was allocated (or empty), drop it. Otherwise, check if we have space in the pool.
+ if (bucketIndex < _buckets.Length)
+ {
+ // Clear the array if the user requests.
+ if (clearArray)
+ {
+ Array.Clear(array, 0, array.Length);
+ }
+
+ // Check to see if the buffer is the correct size for this bucket
+ if (array.Length != _bucketArraySizes[bucketIndex])
+ {
+ throw new ArgumentException(SR.ArgumentException_BufferNotFromPool, nameof(array));
+ }
+
+ // Write through the TLS bucket. If there weren't any buckets, create them
+ // and store this array into it. If there were, store this into it, and
+ // if there was a previous one there, push that to the global stack. This
+ // helps to keep LIFO access such that the most recently pushed stack will
+ // be in TLS and the first to be popped next.
+ T[][] tlsBuckets = t_tlsBuckets;
+ if (tlsBuckets == null)
+ {
+ t_tlsBuckets = tlsBuckets = new T[NumBuckets][];
+ tlsBuckets[bucketIndex] = array;
+ }
+ else
+ {
+ T[] prev = tlsBuckets[bucketIndex];
+ tlsBuckets[bucketIndex] = array;
+ if (prev != null)
+ {
+ PerCoreLockedStacks bucket = _buckets[bucketIndex] ?? CreatePerCoreLockedStacks(bucketIndex);
+ bucket.TryPush(prev);
+ }
+ }
+ }
+
+ // Log that the buffer was returned
+ ArrayPoolEventSource log = ArrayPoolEventSource.Log;
+ if (log.IsEnabled())
+ {
+ log.BufferReturned(array.GetHashCode(), array.Length, Id);
+ }
+ }
+
+ /// <summary>
+ /// Stores a set of stacks of arrays, with one stack per core.
+ /// </summary>
+ private sealed class PerCoreLockedStacks
+ {
+ /// <summary>The stacks.</summary>
+ private readonly LockedStack[] _perCoreStacks;
+
+ /// <summary>Initializes the stacks.</summary>
+ public PerCoreLockedStacks()
+ {
+ // Create the stacks. We create as many as there are processors, limited by our max.
+ var stacks = new LockedStack[Math.Min(Environment.ProcessorCount, MaxPerCorePerArraySizeStacks)];
+ for (int i = 0; i < stacks.Length; i++)
+ {
+ stacks[i] = new LockedStack();
+ }
+ _perCoreStacks = stacks;
+ }
+
+ /// <summary>Try to push the array into the stacks. If each is full when it's tested, the array will be dropped.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void TryPush(T[] array)
+ {
+ // Try to push on to the associated stack first. If that fails,
+ // round-robin through the other stacks.
+ LockedStack[] stacks = _perCoreStacks;
+ int index = Environment.CurrentExecutionId % stacks.Length;
+ for (int i = 0; i < stacks.Length; i++)
+ {
+ if (stacks[index].TryPush(array)) return;
+ if (++index == stacks.Length) index = 0;
+ }
+ }
+
+ /// <summary>Try to get an array from the stacks. If each is empty when it's tested, null will be returned.</summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T[] TryPop()
+ {
+ // Try to pop from the associated stack first. If that fails,
+ // round-robin through the other stacks.
+ T[] arr;
+ LockedStack[] stacks = _perCoreStacks;
+ int index = Environment.CurrentExecutionId % stacks.Length;
+ for (int i = 0; i < stacks.Length; i++)
+ {
+ if ((arr = stacks[index].TryPop()) != null) return arr;
+ if (++index == stacks.Length) index = 0;
+ }
+ return null;
+ }
+ }
+
+ /// <summary>Provides a simple stack of arrays, protected by a lock.</summary>
+ private sealed class LockedStack
+ {
+ private readonly T[][] _arrays = new T[MaxBuffersPerArraySizePerCore][];
+ private int _count;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryPush(T[] array)
+ {
+ bool enqueued = false;
+ Monitor.Enter(this);
+ if (_count < MaxBuffersPerArraySizePerCore)
+ {
+ _arrays[_count++] = array;
+ enqueued = true;
+ }
+ Monitor.Exit(this);
+ return enqueued;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T[] TryPop()
+ {
+ T[] arr = null;
+ Monitor.Enter(this);
+ if (_count > 0)
+ {
+ arr = _arrays[--_count];
+ _arrays[_count] = null;
+ }
+ Monitor.Exit(this);
+ return arr;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Buffers/Utilities.cs b/src/mscorlib/shared/System/Buffers/Utilities.cs
new file mode 100644
index 0000000000..4f115fe9dd
--- /dev/null
+++ b/src/mscorlib/shared/System/Buffers/Utilities.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace System.Buffers
+{
+ internal static class Utilities
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static int SelectBucketIndex(int bufferSize)
+ {
+ uint bitsRemaining = ((uint)bufferSize - 1) >> 4;
+
+ int poolIndex = 0;
+ if (bitsRemaining > 0xFFFF) { bitsRemaining >>= 16; poolIndex = 16; }
+ if (bitsRemaining > 0xFF) { bitsRemaining >>= 8; poolIndex += 8; }
+ if (bitsRemaining > 0xF) { bitsRemaining >>= 4; poolIndex += 4; }
+ if (bitsRemaining > 0x3) { bitsRemaining >>= 2; poolIndex += 2; }
+ if (bitsRemaining > 0x1) { bitsRemaining >>= 1; poolIndex += 1; }
+
+ return poolIndex + (int)bitsRemaining;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static int GetMaxSizeForBucket(int binIndex)
+ {
+ int maxSize = 16 << binIndex;
+ Debug.Assert(maxSize >= 0);
+ return maxSize;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/CLSCompliantAttribute.cs b/src/mscorlib/shared/System/CLSCompliantAttribute.cs
new file mode 100644
index 0000000000..e03600d132
--- /dev/null
+++ b/src/mscorlib/shared/System/CLSCompliantAttribute.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Container for assemblies.
+**
+**
+=============================================================================*/
+
+namespace System
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)]
+ public sealed class CLSCompliantAttribute : Attribute
+ {
+ private bool _compliant;
+
+ public CLSCompliantAttribute(bool isCompliant)
+ {
+ _compliant = isCompliant;
+ }
+ public bool IsCompliant
+ {
+ get
+ {
+ return _compliant;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Char.cs b/src/mscorlib/shared/System/Char.cs
new file mode 100644
index 0000000000..2b3133a669
--- /dev/null
+++ b/src/mscorlib/shared/System/Char.cs
@@ -0,0 +1,1122 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: This is the value class representing a Unicode character
+** Char methods until we create this functionality.
+**
+**
+===========================================================*/
+
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace System
+{
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Char : IComparable, IComparable<Char>, IEquatable<Char>, IConvertible
+ {
+ //
+ // Member Variables
+ //
+ internal char m_value;
+
+ //
+ // Public Constants
+ //
+ // The maximum character value.
+ public const char MaxValue = (char)0xFFFF;
+ // The minimum character value.
+ public const char MinValue = (char)0x00;
+
+ // Unicode category values from Unicode U+0000 ~ U+00FF. Store them in byte[] array to save space.
+ private static readonly byte[] s_categoryForLatin1 = {
+ (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0000 - 0007
+ (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0008 - 000F
+ (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0010 - 0017
+ (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0018 - 001F
+ (byte)UnicodeCategory.SpaceSeparator, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, // 0020 - 0027
+ (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.DashPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, // 0028 - 002F
+ (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, // 0030 - 0037
+ (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.DecimalDigitNumber, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherPunctuation, // 0038 - 003F
+ (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 0040 - 0047
+ (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 0048 - 004F
+ (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 0050 - 0057
+ (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.ConnectorPunctuation, // 0058 - 005F
+ (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 0060 - 0067
+ (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 0068 - 006F
+ (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 0070 - 0077
+ (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.OpenPunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.ClosePunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.Control, // 0078 - 007F
+ (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0080 - 0087
+ (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0088 - 008F
+ (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0090 - 0097
+ (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, (byte)UnicodeCategory.Control, // 0098 - 009F
+ (byte)UnicodeCategory.SpaceSeparator, (byte)UnicodeCategory.OtherPunctuation, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.CurrencySymbol, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.OtherSymbol, // 00A0 - 00A7
+ (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.InitialQuotePunctuation, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.DashPunctuation, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.ModifierSymbol, // 00A8 - 00AF
+ (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.MathSymbol, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.OtherSymbol, (byte)UnicodeCategory.OtherPunctuation, // 00B0 - 00B7
+ (byte)UnicodeCategory.ModifierSymbol, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.FinalQuotePunctuation, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherNumber, (byte)UnicodeCategory.OtherPunctuation, // 00B8 - 00BF
+ (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 00C0 - 00C7
+ (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, // 00C8 - 00CF
+ (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.MathSymbol, // 00D0 - 00D7
+ (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.UppercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00D8 - 00DF
+ (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00E0 - 00E7
+ (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00E8 - 00EF
+ (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.MathSymbol, // 00F0 - 00F7
+ (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, (byte)UnicodeCategory.LowercaseLetter, // 00F8 - 00FF
+ };
+
+ // Return true for all characters below or equal U+00ff, which is ASCII + Latin-1 Supplement.
+ private static bool IsLatin1(char ch)
+ {
+ return (ch <= '\x00ff');
+ }
+
+ // Return true for all characters below or equal U+007f, which is ASCII.
+ private static bool IsAscii(char ch)
+ {
+ return (ch <= '\x007f');
+ }
+
+ // Return the Unicode category for Unicode character <= 0x00ff.
+ private static UnicodeCategory GetLatin1UnicodeCategory(char ch)
+ {
+ Debug.Assert(IsLatin1(ch), "Char.GetLatin1UnicodeCategory(): ch should be <= 007f");
+ return (UnicodeCategory)(s_categoryForLatin1[(int)ch]);
+ }
+
+ //
+ // Private Constants
+ //
+
+ //
+ // Overriden Instance Methods
+ //
+
+ // Calculate a hashcode for a 2 byte Unicode character.
+ public override int GetHashCode()
+ {
+ return (int)m_value | ((int)m_value << 16);
+ }
+
+ // Used for comparing two boxed Char objects.
+ //
+ public override bool Equals(Object obj)
+ {
+ if (!(obj is Char))
+ {
+ return false;
+ }
+ return (m_value == ((Char)obj).m_value);
+ }
+
+ [System.Runtime.Versioning.NonVersionable]
+ public bool Equals(Char obj)
+ {
+ return m_value == obj;
+ }
+
+ // Compares this object to another object, returning an integer that
+ // indicates the relationship.
+ // Returns a value less than zero if this object
+ // null is considered to be less than any instance.
+ // If object is not of type Char, this method throws an ArgumentException.
+ //
+ [Pure]
+ public int CompareTo(Object value)
+ {
+ if (value == null)
+ {
+ return 1;
+ }
+ if (!(value is Char))
+ {
+ throw new ArgumentException(SR.Arg_MustBeChar);
+ }
+
+ return (m_value - ((Char)value).m_value);
+ }
+
+ [Pure]
+ public int CompareTo(Char value)
+ {
+ return (m_value - value);
+ }
+
+ // Overrides System.Object.ToString.
+ [Pure]
+ public override String ToString()
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return Char.ToString(m_value);
+ }
+
+ [Pure]
+ public String ToString(IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return Char.ToString(m_value);
+ }
+
+ //
+ // Formatting Methods
+ //
+
+ /*===================================ToString===================================
+ **This static methods takes a character and returns the String representation of it.
+ ==============================================================================*/
+ // Provides a string representation of a character.
+ [Pure]
+ public static string ToString(char c) => string.CreateFromChar(c);
+
+ public static char Parse(String s)
+ {
+ if (s == null)
+ {
+ throw new ArgumentNullException(nameof(s));
+ }
+ Contract.EndContractBlock();
+
+ if (s.Length != 1)
+ {
+ throw new FormatException(SR.Format_NeedSingleChar);
+ }
+ return s[0];
+ }
+
+ public static bool TryParse(String s, out Char result)
+ {
+ result = '\0';
+ if (s == null)
+ {
+ return false;
+ }
+ if (s.Length != 1)
+ {
+ return false;
+ }
+ result = s[0];
+ return true;
+ }
+
+ //
+ // Static Methods
+ //
+ /*=================================ISDIGIT======================================
+ **A wrapper for Char. Returns a boolean indicating whether **
+ **character c is considered to be a digit. **
+ ==============================================================================*/
+ // Determines whether a character is a digit.
+ [Pure]
+ public static bool IsDigit(char c)
+ {
+ if (IsLatin1(c))
+ {
+ return (c >= '0' && c <= '9');
+ }
+ return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.DecimalDigitNumber);
+ }
+
+
+ /*=================================CheckLetter=====================================
+ ** Check if the specified UnicodeCategory belongs to the letter categories.
+ ==============================================================================*/
+ internal static bool CheckLetter(UnicodeCategory uc)
+ {
+ switch (uc)
+ {
+ case (UnicodeCategory.UppercaseLetter):
+ case (UnicodeCategory.LowercaseLetter):
+ case (UnicodeCategory.TitlecaseLetter):
+ case (UnicodeCategory.ModifierLetter):
+ case (UnicodeCategory.OtherLetter):
+ return (true);
+ }
+ return (false);
+ }
+
+ /*=================================ISLETTER=====================================
+ **A wrapper for Char. Returns a boolean indicating whether **
+ **character c is considered to be a letter. **
+ ==============================================================================*/
+ // Determines whether a character is a letter.
+ [Pure]
+ public static bool IsLetter(char c)
+ {
+ if (IsLatin1(c))
+ {
+ if (IsAscii(c))
+ {
+ c |= (char)0x20;
+ return ((c >= 'a' && c <= 'z'));
+ }
+ return (CheckLetter(GetLatin1UnicodeCategory(c)));
+ }
+ return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(c)));
+ }
+
+ private static bool IsWhiteSpaceLatin1(char c)
+ {
+ // There are characters which belong to UnicodeCategory.Control but are considered as white spaces.
+ // We use code point comparisons for these characters here as a temporary fix.
+
+ // U+0009 = <control> HORIZONTAL TAB
+ // U+000a = <control> LINE FEED
+ // U+000b = <control> VERTICAL TAB
+ // U+000c = <contorl> FORM FEED
+ // U+000d = <control> CARRIAGE RETURN
+ // U+0085 = <control> NEXT LINE
+ // U+00a0 = NO-BREAK SPACE
+ if ((c == ' ') || (c >= '\x0009' && c <= '\x000d') || c == '\x00a0' || c == '\x0085')
+ {
+ return (true);
+ }
+ return (false);
+ }
+
+ /*===============================ISWHITESPACE===================================
+ **A wrapper for Char. Returns a boolean indicating whether **
+ **character c is considered to be a whitespace character. **
+ ==============================================================================*/
+ // Determines whether a character is whitespace.
+ [Pure]
+ public static bool IsWhiteSpace(char c)
+ {
+ if (IsLatin1(c))
+ {
+ return (IsWhiteSpaceLatin1(c));
+ }
+ return CharUnicodeInfo.IsWhiteSpace(c);
+ }
+
+
+ /*===================================IsUpper====================================
+ **Arguments: c -- the characater to be checked.
+ **Returns: True if c is an uppercase character.
+ ==============================================================================*/
+ // Determines whether a character is upper-case.
+ [Pure]
+ public static bool IsUpper(char c)
+ {
+ if (IsLatin1(c))
+ {
+ if (IsAscii(c))
+ {
+ return (c >= 'A' && c <= 'Z');
+ }
+ return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter);
+ }
+ return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.UppercaseLetter);
+ }
+
+ /*===================================IsLower====================================
+ **Arguments: c -- the characater to be checked.
+ **Returns: True if c is an lowercase character.
+ ==============================================================================*/
+ // Determines whether a character is lower-case.
+ [Pure]
+ public static bool IsLower(char c)
+ {
+ if (IsLatin1(c))
+ {
+ if (IsAscii(c))
+ {
+ return (c >= 'a' && c <= 'z');
+ }
+ return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter);
+ }
+ return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.LowercaseLetter);
+ }
+
+ internal static bool CheckPunctuation(UnicodeCategory uc)
+ {
+ switch (uc)
+ {
+ case UnicodeCategory.ConnectorPunctuation:
+ case UnicodeCategory.DashPunctuation:
+ case UnicodeCategory.OpenPunctuation:
+ case UnicodeCategory.ClosePunctuation:
+ case UnicodeCategory.InitialQuotePunctuation:
+ case UnicodeCategory.FinalQuotePunctuation:
+ case UnicodeCategory.OtherPunctuation:
+ return (true);
+ }
+ return (false);
+ }
+
+
+ /*================================IsPunctuation=================================
+ **Arguments: c -- the characater to be checked.
+ **Returns: True if c is an punctuation mark
+ ==============================================================================*/
+ // Determines whether a character is a punctuation mark.
+ [Pure]
+ public static bool IsPunctuation(char c)
+ {
+ if (IsLatin1(c))
+ {
+ return (CheckPunctuation(GetLatin1UnicodeCategory(c)));
+ }
+ return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(c)));
+ }
+
+ /*=================================CheckLetterOrDigit=====================================
+ ** Check if the specified UnicodeCategory belongs to the letter or digit categories.
+ ==============================================================================*/
+ internal static bool CheckLetterOrDigit(UnicodeCategory uc)
+ {
+ switch (uc)
+ {
+ case UnicodeCategory.UppercaseLetter:
+ case UnicodeCategory.LowercaseLetter:
+ case UnicodeCategory.TitlecaseLetter:
+ case UnicodeCategory.ModifierLetter:
+ case UnicodeCategory.OtherLetter:
+ case UnicodeCategory.DecimalDigitNumber:
+ return (true);
+ }
+ return (false);
+ }
+
+ // Determines whether a character is a letter or a digit.
+ [Pure]
+ public static bool IsLetterOrDigit(char c)
+ {
+ if (IsLatin1(c))
+ {
+ return (CheckLetterOrDigit(GetLatin1UnicodeCategory(c)));
+ }
+ return (CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(c)));
+ }
+
+ /*===================================ToUpper====================================
+ **
+ ==============================================================================*/
+ // Converts a character to upper-case for the specified culture.
+ // <;<;Not fully implemented>;>;
+ public static char ToUpper(char c, CultureInfo culture)
+ {
+ if (culture == null)
+ throw new ArgumentNullException(nameof(culture));
+ Contract.EndContractBlock();
+ return culture.TextInfo.ToUpper(c);
+ }
+
+ /*=================================TOUPPER======================================
+ **A wrapper for Char.toUpperCase. Converts character c to its **
+ **uppercase equivalent. If c is already an uppercase character or is not an **
+ **alphabetic, nothing happens. **
+ ==============================================================================*/
+ // Converts a character to upper-case for the default culture.
+ //
+ public static char ToUpper(char c)
+ {
+ return ToUpper(c, CultureInfo.CurrentCulture);
+ }
+
+
+ // Converts a character to upper-case for invariant culture.
+ public static char ToUpperInvariant(char c)
+ {
+ return ToUpper(c, CultureInfo.InvariantCulture);
+ }
+
+
+ /*===================================ToLower====================================
+ **
+ ==============================================================================*/
+ // Converts a character to lower-case for the specified culture.
+ // <;<;Not fully implemented>;>;
+ public static char ToLower(char c, CultureInfo culture)
+ {
+ if (culture == null)
+ throw new ArgumentNullException(nameof(culture));
+ Contract.EndContractBlock();
+ return culture.TextInfo.ToLower(c);
+ }
+
+ /*=================================TOLOWER======================================
+ **A wrapper for Char.toLowerCase. Converts character c to its **
+ **lowercase equivalent. If c is already a lowercase character or is not an **
+ **alphabetic, nothing happens. **
+ ==============================================================================*/
+ // Converts a character to lower-case for the default culture.
+ public static char ToLower(char c)
+ {
+ return ToLower(c, CultureInfo.CurrentCulture);
+ }
+
+
+ // Converts a character to lower-case for invariant culture.
+ public static char ToLowerInvariant(char c)
+ {
+ return ToLower(c, CultureInfo.InvariantCulture);
+ }
+
+
+ //
+ // IConvertible implementation
+ //
+ [Pure]
+ public TypeCode GetTypeCode()
+ {
+ return TypeCode.Char;
+ }
+
+
+ bool IConvertible.ToBoolean(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Boolean"));
+ }
+
+ char IConvertible.ToChar(IFormatProvider provider)
+ {
+ return m_value;
+ }
+
+ sbyte IConvertible.ToSByte(IFormatProvider provider)
+ {
+ return Convert.ToSByte(m_value);
+ }
+
+ byte IConvertible.ToByte(IFormatProvider provider)
+ {
+ return Convert.ToByte(m_value);
+ }
+
+ short IConvertible.ToInt16(IFormatProvider provider)
+ {
+ return Convert.ToInt16(m_value);
+ }
+
+ ushort IConvertible.ToUInt16(IFormatProvider provider)
+ {
+ return Convert.ToUInt16(m_value);
+ }
+
+ int IConvertible.ToInt32(IFormatProvider provider)
+ {
+ return Convert.ToInt32(m_value);
+ }
+
+ uint IConvertible.ToUInt32(IFormatProvider provider)
+ {
+ return Convert.ToUInt32(m_value);
+ }
+
+ long IConvertible.ToInt64(IFormatProvider provider)
+ {
+ return Convert.ToInt64(m_value);
+ }
+
+ ulong IConvertible.ToUInt64(IFormatProvider provider)
+ {
+ return Convert.ToUInt64(m_value);
+ }
+
+ float IConvertible.ToSingle(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Single"));
+ }
+
+ double IConvertible.ToDouble(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Double"));
+ }
+
+ Decimal IConvertible.ToDecimal(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "Decimal"));
+ }
+
+ DateTime IConvertible.ToDateTime(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Char", "DateTime"));
+ }
+
+ Object IConvertible.ToType(Type type, IFormatProvider provider)
+ {
+ return Convert.DefaultToType((IConvertible)this, type, provider);
+ }
+ public static bool IsControl(char c)
+ {
+ if (IsLatin1(c))
+ {
+ return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control);
+ }
+ return (CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.Control);
+ }
+
+ public static bool IsControl(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ return (GetLatin1UnicodeCategory(c) == UnicodeCategory.Control);
+ }
+ return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.Control);
+ }
+
+
+ public static bool IsDigit(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ return (c >= '0' && c <= '9');
+ }
+ return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.DecimalDigitNumber);
+ }
+
+ public static bool IsLetter(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ if (IsAscii(c))
+ {
+ c |= (char)0x20;
+ return ((c >= 'a' && c <= 'z'));
+ }
+ return (CheckLetter(GetLatin1UnicodeCategory(c)));
+ }
+ return (CheckLetter(CharUnicodeInfo.GetUnicodeCategory(s, index)));
+ }
+
+ public static bool IsLetterOrDigit(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ return CheckLetterOrDigit(GetLatin1UnicodeCategory(c));
+ }
+ return CheckLetterOrDigit(CharUnicodeInfo.GetUnicodeCategory(s, index));
+ }
+
+ public static bool IsLower(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ if (IsAscii(c))
+ {
+ return (c >= 'a' && c <= 'z');
+ }
+ return (GetLatin1UnicodeCategory(c) == UnicodeCategory.LowercaseLetter);
+ }
+
+ return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.LowercaseLetter);
+ }
+
+ /*=================================CheckNumber=====================================
+ ** Check if the specified UnicodeCategory belongs to the number categories.
+ ==============================================================================*/
+
+ internal static bool CheckNumber(UnicodeCategory uc)
+ {
+ switch (uc)
+ {
+ case (UnicodeCategory.DecimalDigitNumber):
+ case (UnicodeCategory.LetterNumber):
+ case (UnicodeCategory.OtherNumber):
+ return (true);
+ }
+ return (false);
+ }
+
+ public static bool IsNumber(char c)
+ {
+ if (IsLatin1(c))
+ {
+ if (IsAscii(c))
+ {
+ return (c >= '0' && c <= '9');
+ }
+ return (CheckNumber(GetLatin1UnicodeCategory(c)));
+ }
+ return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(c)));
+ }
+
+ public static bool IsNumber(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ if (IsAscii(c))
+ {
+ return (c >= '0' && c <= '9');
+ }
+ return (CheckNumber(GetLatin1UnicodeCategory(c)));
+ }
+ return (CheckNumber(CharUnicodeInfo.GetUnicodeCategory(s, index)));
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // IsPunctuation
+ //
+ // Determines if the given character is a punctuation character.
+ //
+ ////////////////////////////////////////////////////////////////////////
+
+ public static bool IsPunctuation(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ return (CheckPunctuation(GetLatin1UnicodeCategory(c)));
+ }
+ return (CheckPunctuation(CharUnicodeInfo.GetUnicodeCategory(s, index)));
+ }
+
+
+ /*================================= CheckSeparator ============================
+ ** Check if the specified UnicodeCategory belongs to the seprator categories.
+ ==============================================================================*/
+
+ internal static bool CheckSeparator(UnicodeCategory uc)
+ {
+ switch (uc)
+ {
+ case UnicodeCategory.SpaceSeparator:
+ case UnicodeCategory.LineSeparator:
+ case UnicodeCategory.ParagraphSeparator:
+ return (true);
+ }
+ return (false);
+ }
+
+ private static bool IsSeparatorLatin1(char c)
+ {
+ // U+00a0 = NO-BREAK SPACE
+ // There is no LineSeparator or ParagraphSeparator in Latin 1 range.
+ return (c == '\x0020' || c == '\x00a0');
+ }
+
+ public static bool IsSeparator(char c)
+ {
+ if (IsLatin1(c))
+ {
+ return (IsSeparatorLatin1(c));
+ }
+ return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(c)));
+ }
+
+ public static bool IsSeparator(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ return (IsSeparatorLatin1(c));
+ }
+ return (CheckSeparator(CharUnicodeInfo.GetUnicodeCategory(s, index)));
+ }
+
+ [Pure]
+ public static bool IsSurrogate(char c)
+ {
+ return (c >= HIGH_SURROGATE_START && c <= LOW_SURROGATE_END);
+ }
+
+ [Pure]
+ public static bool IsSurrogate(String s, int index)
+ {
+ if (s == null)
+ {
+ throw new ArgumentNullException(nameof(s));
+ }
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ return (IsSurrogate(s[index]));
+ }
+
+ /*================================= CheckSymbol ============================
+ ** Check if the specified UnicodeCategory belongs to the symbol categories.
+ ==============================================================================*/
+
+ internal static bool CheckSymbol(UnicodeCategory uc)
+ {
+ switch (uc)
+ {
+ case (UnicodeCategory.MathSymbol):
+ case (UnicodeCategory.CurrencySymbol):
+ case (UnicodeCategory.ModifierSymbol):
+ case (UnicodeCategory.OtherSymbol):
+ return (true);
+ }
+ return (false);
+ }
+
+ public static bool IsSymbol(char c)
+ {
+ if (IsLatin1(c))
+ {
+ return (CheckSymbol(GetLatin1UnicodeCategory(c)));
+ }
+ return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(c)));
+ }
+
+ public static bool IsSymbol(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ return (CheckSymbol(GetLatin1UnicodeCategory(c)));
+ }
+ return (CheckSymbol(CharUnicodeInfo.GetUnicodeCategory(s, index)));
+ }
+
+
+ public static bool IsUpper(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ char c = s[index];
+ if (IsLatin1(c))
+ {
+ if (IsAscii(c))
+ {
+ return (c >= 'A' && c <= 'Z');
+ }
+ return (GetLatin1UnicodeCategory(c) == UnicodeCategory.UppercaseLetter);
+ }
+
+ return (CharUnicodeInfo.GetUnicodeCategory(s, index) == UnicodeCategory.UppercaseLetter);
+ }
+
+ public static bool IsWhiteSpace(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+
+ if (IsLatin1(s[index]))
+ {
+ return IsWhiteSpaceLatin1(s[index]);
+ }
+
+ return CharUnicodeInfo.IsWhiteSpace(s, index);
+ }
+
+ public static UnicodeCategory GetUnicodeCategory(char c)
+ {
+ if (IsLatin1(c))
+ {
+ return (GetLatin1UnicodeCategory(c));
+ }
+ return CharUnicodeInfo.InternalGetUnicodeCategory(c);
+ }
+
+ public static UnicodeCategory GetUnicodeCategory(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ if (IsLatin1(s[index]))
+ {
+ return (GetLatin1UnicodeCategory(s[index]));
+ }
+ return CharUnicodeInfo.InternalGetUnicodeCategory(s, index);
+ }
+
+ public static double GetNumericValue(char c)
+ {
+ return CharUnicodeInfo.GetNumericValue(c);
+ }
+
+ public static double GetNumericValue(String s, int index)
+ {
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+ if (((uint)index) >= ((uint)s.Length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ return CharUnicodeInfo.GetNumericValue(s, index);
+ }
+
+
+ /*================================= IsHighSurrogate ============================
+ ** Check if a char is a high surrogate.
+ ==============================================================================*/
+ [Pure]
+ public static bool IsHighSurrogate(char c)
+ {
+ return ((c >= CharUnicodeInfo.HIGH_SURROGATE_START) && (c <= CharUnicodeInfo.HIGH_SURROGATE_END));
+ }
+
+ [Pure]
+ public static bool IsHighSurrogate(String s, int index)
+ {
+ if (s == null)
+ {
+ throw new ArgumentNullException(nameof(s));
+ }
+ if (index < 0 || index >= s.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ return (IsHighSurrogate(s[index]));
+ }
+
+ /*================================= IsLowSurrogate ============================
+ ** Check if a char is a low surrogate.
+ ==============================================================================*/
+ [Pure]
+ public static bool IsLowSurrogate(char c)
+ {
+ return ((c >= CharUnicodeInfo.LOW_SURROGATE_START) && (c <= CharUnicodeInfo.LOW_SURROGATE_END));
+ }
+
+ [Pure]
+ public static bool IsLowSurrogate(String s, int index)
+ {
+ if (s == null)
+ {
+ throw new ArgumentNullException(nameof(s));
+ }
+ if (index < 0 || index >= s.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ return (IsLowSurrogate(s[index]));
+ }
+
+ /*================================= IsSurrogatePair ============================
+ ** Check if the string specified by the index starts with a surrogate pair.
+ ==============================================================================*/
+ [Pure]
+ public static bool IsSurrogatePair(String s, int index)
+ {
+ if (s == null)
+ {
+ throw new ArgumentNullException(nameof(s));
+ }
+ if (index < 0 || index >= s.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Contract.EndContractBlock();
+ if (index + 1 < s.Length)
+ {
+ return (IsSurrogatePair(s[index], s[index + 1]));
+ }
+ return (false);
+ }
+
+ [Pure]
+ public static bool IsSurrogatePair(char highSurrogate, char lowSurrogate)
+ {
+ return ((highSurrogate >= CharUnicodeInfo.HIGH_SURROGATE_START && highSurrogate <= CharUnicodeInfo.HIGH_SURROGATE_END) &&
+ (lowSurrogate >= CharUnicodeInfo.LOW_SURROGATE_START && lowSurrogate <= CharUnicodeInfo.LOW_SURROGATE_END));
+ }
+
+ internal const int UNICODE_PLANE00_END = 0x00ffff;
+ // The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff.
+ internal const int UNICODE_PLANE01_START = 0x10000;
+ // The end codepoint for Unicode plane 16. This is the maximum code point value allowed for Unicode.
+ // Plane 16 contains 0x100000 ~ 0x10ffff.
+ internal const int UNICODE_PLANE16_END = 0x10ffff;
+
+ internal const int HIGH_SURROGATE_START = 0x00d800;
+ internal const int LOW_SURROGATE_END = 0x00dfff;
+
+
+
+ /*================================= ConvertFromUtf32 ============================
+ ** Convert an UTF32 value into a surrogate pair.
+ ==============================================================================*/
+
+ public static String ConvertFromUtf32(int utf32)
+ {
+ // For UTF32 values from U+00D800 ~ U+00DFFF, we should throw. They
+ // are considered as irregular code unit sequence, but they are not illegal.
+ if ((utf32 < 0 || utf32 > UNICODE_PLANE16_END) || (utf32 >= HIGH_SURROGATE_START && utf32 <= LOW_SURROGATE_END))
+ {
+ throw new ArgumentOutOfRangeException(nameof(utf32), SR.ArgumentOutOfRange_InvalidUTF32);
+ }
+ Contract.EndContractBlock();
+
+ if (utf32 < UNICODE_PLANE01_START)
+ {
+ // This is a BMP character.
+ return (Char.ToString((char)utf32));
+ }
+
+ unsafe
+ {
+ // This is a supplementary character. Convert it to a surrogate pair in UTF-16.
+ utf32 -= UNICODE_PLANE01_START;
+ uint surrogate = 0; // allocate 2 chars worth of stack space
+ char* address = (char*)&surrogate;
+ address[0] = (char)((utf32 / 0x400) + (int)CharUnicodeInfo.HIGH_SURROGATE_START);
+ address[1] = (char)((utf32 % 0x400) + (int)CharUnicodeInfo.LOW_SURROGATE_START);
+ return new string(address, 0, 2);
+ }
+ }
+
+
+ /*=============================ConvertToUtf32===================================
+ ** Convert a surrogate pair to UTF32 value
+ ==============================================================================*/
+
+ public static int ConvertToUtf32(char highSurrogate, char lowSurrogate)
+ {
+ if (!IsHighSurrogate(highSurrogate))
+ {
+ throw new ArgumentOutOfRangeException(nameof(highSurrogate), SR.ArgumentOutOfRange_InvalidHighSurrogate);
+ }
+ if (!IsLowSurrogate(lowSurrogate))
+ {
+ throw new ArgumentOutOfRangeException(nameof(lowSurrogate), SR.ArgumentOutOfRange_InvalidLowSurrogate);
+ }
+ Contract.EndContractBlock();
+ return (((highSurrogate - CharUnicodeInfo.HIGH_SURROGATE_START) * 0x400) + (lowSurrogate - CharUnicodeInfo.LOW_SURROGATE_START) + UNICODE_PLANE01_START);
+ }
+
+ /*=============================ConvertToUtf32===================================
+ ** Convert a character or a surrogate pair starting at index of the specified string
+ ** to UTF32 value.
+ ** The char pointed by index should be a surrogate pair or a BMP character.
+ ** This method throws if a high-surrogate is not followed by a low surrogate.
+ ** This method throws if a low surrogate is seen without preceding a high-surrogate.
+ ==============================================================================*/
+
+ public static int ConvertToUtf32(String s, int index)
+ {
+ if (s == null)
+ {
+ throw new ArgumentNullException(nameof(s));
+ }
+
+ if (index < 0 || index >= s.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
+ }
+ Contract.EndContractBlock();
+ // Check if the character at index is a high surrogate.
+ int temp1 = (int)s[index] - CharUnicodeInfo.HIGH_SURROGATE_START;
+ if (temp1 >= 0 && temp1 <= 0x7ff)
+ {
+ // Found a surrogate char.
+ if (temp1 <= 0x3ff)
+ {
+ // Found a high surrogate.
+ if (index < s.Length - 1)
+ {
+ int temp2 = (int)s[index + 1] - CharUnicodeInfo.LOW_SURROGATE_START;
+ if (temp2 >= 0 && temp2 <= 0x3ff)
+ {
+ // Found a low surrogate.
+ return ((temp1 * 0x400) + temp2 + UNICODE_PLANE01_START);
+ }
+ else
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s));
+ }
+ }
+ else
+ {
+ // Found a high surrogate at the end of the string.
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidHighSurrogate, index), nameof(s));
+ }
+ }
+ else
+ {
+ // Find a low surrogate at the character pointed by index.
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidLowSurrogate, index), nameof(s));
+ }
+ }
+ // Not a high-surrogate or low-surrogate. Genereate the UTF32 value for the BMP characters.
+ return ((int)s[index]);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/CharEnumerator.cs b/src/mscorlib/shared/System/CharEnumerator.cs
new file mode 100644
index 0000000000..ea9915a7c4
--- /dev/null
+++ b/src/mscorlib/shared/System/CharEnumerator.cs
@@ -0,0 +1,80 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Enumerates the characters on a string. skips range
+** checks.
+**
+**
+============================================================*/
+
+using System.Collections;
+using System.Collections.Generic;
+
+namespace System
+{
+ public sealed class CharEnumerator : IEnumerator, IEnumerator<char>, IDisposable, ICloneable
+ {
+ private String _str;
+ private int _index;
+ private char _currentElement;
+
+ internal CharEnumerator(String str)
+ {
+ _str = str;
+ _index = -1;
+ }
+
+ public object Clone()
+ {
+ return MemberwiseClone();
+ }
+
+ public bool MoveNext()
+ {
+ if (_index < (_str.Length - 1))
+ {
+ _index++;
+ _currentElement = _str[_index];
+ return true;
+ }
+ else
+ _index = _str.Length;
+ return false;
+ }
+
+ public void Dispose()
+ {
+ if (_str != null)
+ _index = _str.Length;
+ _str = null;
+ }
+
+ Object IEnumerator.Current
+ {
+ get { return Current; }
+ }
+
+ public char Current
+ {
+ get
+ {
+ if (_index == -1)
+ throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted);
+ if (_index >= _str.Length)
+ throw new InvalidOperationException(SR.InvalidOperation_EnumEnded);
+ return _currentElement;
+ }
+ }
+
+ public void Reset()
+ {
+ _currentElement = (char)0;
+ _index = -1;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/DictionaryEntry.cs b/src/mscorlib/shared/System/Collections/DictionaryEntry.cs
new file mode 100644
index 0000000000..290306d006
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/DictionaryEntry.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.ComponentModel;
+
+namespace System.Collections
+{
+ // A DictionaryEntry holds a key and a value from a dictionary.
+ // It is returned by IDictionaryEnumerator::GetEntry().
+ [Serializable]
+ public struct DictionaryEntry
+ {
+ private Object _key;
+ private Object _value;
+
+ // Constructs a new DictionaryEnumerator by setting the Key
+ // and Value fields appropriately.
+ public DictionaryEntry(Object key, Object value)
+ {
+ _key = key;
+ _value = value;
+ }
+
+ public Object Key
+ {
+ get
+ {
+ return _key;
+ }
+
+ set
+ {
+ _key = value;
+ }
+ }
+
+ public Object Value
+ {
+ get
+ {
+ return _value;
+ }
+
+ set
+ {
+ _value = value;
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void Deconstruct(out object key, out object value)
+ {
+ key = Key;
+ value = Value;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/ICollection.cs b/src/mscorlib/shared/System/Collections/Generic/ICollection.cs
new file mode 100644
index 0000000000..52852aa1fb
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/ICollection.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.CompilerServices;
+using System.Diagnostics.Contracts;
+
+namespace System.Collections.Generic
+{
+ // Base interface for all collections, defining enumerators, size, and
+ // synchronization methods.
+ public interface ICollection<T> : IEnumerable<T>
+ {
+ // Number of items in the collections.
+ int Count { get; }
+
+ bool IsReadOnly { get; }
+
+ void Add(T item);
+
+ void Clear();
+
+ bool Contains(T item);
+
+ // CopyTo copies a collection into an Array, starting at a particular
+ // index into the array.
+ //
+ void CopyTo(T[] array, int arrayIndex);
+
+ //void CopyTo(int sourceIndex, T[] destinationArray, int destinationIndex, int count);
+
+ bool Remove(T item);
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/IComparer.cs b/src/mscorlib/shared/System/Collections/Generic/IComparer.cs
new file mode 100644
index 0000000000..713d499cc8
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/IComparer.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.Collections.Generic
+{
+ // The generic IComparer interface implements a method that compares
+ // two objects. It is used in conjunction with the Sort and
+ // BinarySearch methods on the Array, List, and SortedList classes.
+ public interface IComparer<in T>
+ {
+ // Compares two objects. An implementation of this method must return a
+ // value less than zero if x is less than y, zero if x is equal to y, or a
+ // value greater than zero if x is greater than y.
+ //
+ int Compare(T x, T y);
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/IDictionary.cs b/src/mscorlib/shared/System/Collections/Generic/IDictionary.cs
new file mode 100644
index 0000000000..a73a2f55bd
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/IDictionary.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Collections.Generic
+{
+ // An IDictionary is a possibly unordered set of key-value pairs.
+ // Keys can be any non-null object. Values can be any object.
+ // You can look up a value in an IDictionary via the default indexed
+ // property, Items.
+ public interface IDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>
+ {
+ // Interfaces are not serializable
+ // The Item property provides methods to read and edit entries
+ // in the Dictionary.
+ TValue this[TKey key]
+ {
+ get;
+ set;
+ }
+
+ // Returns a collections of the keys in this dictionary.
+ ICollection<TKey> Keys
+ {
+ get;
+ }
+
+ // Returns a collections of the values in this dictionary.
+ ICollection<TValue> Values
+ {
+ get;
+ }
+
+ // Returns whether this dictionary contains a particular key.
+ //
+ bool ContainsKey(TKey key);
+
+ // Adds a key-value pair to the dictionary.
+ //
+ void Add(TKey key, TValue value);
+
+ // Removes a particular key from the dictionary.
+ //
+ bool Remove(TKey key);
+
+ bool TryGetValue(TKey key, out TValue value);
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/IEnumerable.cs b/src/mscorlib/shared/System/Collections/Generic/IEnumerable.cs
new file mode 100644
index 0000000000..84264d5cf0
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/IEnumerable.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Diagnostics.Contracts;
+
+namespace System.Collections.Generic
+{
+ // Implement this interface if you need to support foreach semantics.
+ public interface IEnumerable<out T> : IEnumerable
+ {
+ // Returns an IEnumerator for this enumerable Object. The enumerator provides
+ // a simple way to access all the contents of a collection.
+ /// <include file='doc\IEnumerable.uex' path='docs/doc[@for="IEnumerable.GetEnumerator"]/*' />
+ new IEnumerator<T> GetEnumerator();
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/IEnumerator.cs b/src/mscorlib/shared/System/Collections/Generic/IEnumerator.cs
new file mode 100644
index 0000000000..6360576974
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/IEnumerator.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.InteropServices;
+
+namespace System.Collections.Generic
+{
+ // Base interface for all generic enumerators, providing a simple approach
+ // to iterating over a collection.
+ public interface IEnumerator<out T> : IDisposable, IEnumerator
+ {
+ // Returns the current element of the enumeration. The returned value is
+ // undefined before the first call to MoveNext and following a
+ // call to MoveNext that returned false. Multiple calls to
+ // GetCurrent with no intervening calls to MoveNext
+ // will return the same object.
+ //
+ /// <include file='doc\IEnumerator.uex' path='docs/doc[@for="IEnumerator.Current"]/*' />
+ new T Current
+ {
+ get;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/IEqualityComparer.cs b/src/mscorlib/shared/System/Collections/Generic/IEqualityComparer.cs
new file mode 100644
index 0000000000..543bdb5fce
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/IEqualityComparer.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.Collections.Generic
+{
+ // The generic IEqualityComparer interface implements methods to if check two objects are equal
+ // and generate Hashcode for an object.
+ // It is use in Dictionary class.
+ public interface IEqualityComparer<in T>
+ {
+ bool Equals(T x, T y);
+ int GetHashCode(T obj);
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Collections/Generic/IList.cs b/src/mscorlib/shared/System/Collections/Generic/IList.cs
new file mode 100644
index 0000000000..43d6659da9
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/IList.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+using System.Runtime.CompilerServices;
+using System.Diagnostics.Contracts;
+
+namespace System.Collections.Generic
+{
+ // An IList is an ordered collection of objects. The exact ordering
+ // is up to the implementation of the list, ranging from a sorted
+ // order to insertion order.
+ public interface IList<T> : ICollection<T>
+ {
+ // The Item property provides methods to read and edit entries in the List.
+ T this[int index]
+ {
+ get;
+ set;
+ }
+
+ // Returns the index of a particular item, if it is in the list.
+ // Returns -1 if the item isn't in the list.
+ int IndexOf(T item);
+
+ // Inserts value into the list at position index.
+ // index must be non-negative and less than or equal to the
+ // number of elements in the list. If index equals the number
+ // of items in the list, then value is appended to the end.
+ void Insert(int index, T item);
+
+ // Removes the item at position index.
+ void RemoveAt(int index);
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/IReadOnlyCollection.cs b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyCollection.cs
new file mode 100644
index 0000000000..09ee89f035
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyCollection.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+
+namespace System.Collections.Generic
+{
+ // Provides a read-only, covariant view of a generic list.
+ public interface IReadOnlyCollection<out T> : IEnumerable<T>
+ {
+ int Count { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/IReadOnlyDictionary.cs b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyDictionary.cs
new file mode 100644
index 0000000000..169e2958bb
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyDictionary.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Collections.Generic
+{
+ // Provides a read-only view of a generic dictionary.
+ public interface IReadOnlyDictionary<TKey, TValue> : IReadOnlyCollection<KeyValuePair<TKey, TValue>>
+ {
+ bool ContainsKey(TKey key);
+ bool TryGetValue(TKey key, out TValue value);
+
+ TValue this[TKey key] { get; }
+ IEnumerable<TKey> Keys { get; }
+ IEnumerable<TValue> Values { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/IReadOnlyList.cs b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyList.cs
new file mode 100644
index 0000000000..00b5be65ff
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/IReadOnlyList.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
+
+namespace System.Collections.Generic
+{
+ // Provides a read-only, covariant view of a generic list.
+ public interface IReadOnlyList<out T> : IReadOnlyCollection<T>
+ {
+ T this[int index] { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs b/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs
new file mode 100644
index 0000000000..1fca7732ae
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/KeyNotFoundException.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Collections.Generic
+{
+ [Serializable]
+ public class KeyNotFoundException : SystemException
+ {
+ public KeyNotFoundException()
+ : base(SR.Arg_KeyNotFound)
+ {
+ HResult = __HResults.COR_E_KEYNOTFOUND;
+ }
+
+ public KeyNotFoundException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_KEYNOTFOUND;
+ }
+
+ public KeyNotFoundException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_KEYNOTFOUND;
+ }
+
+ protected KeyNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/Generic/KeyValuePair.cs b/src/mscorlib/shared/System/Collections/Generic/KeyValuePair.cs
new file mode 100644
index 0000000000..fc51af25f8
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/Generic/KeyValuePair.cs
@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.ComponentModel;
+using System.Text;
+
+namespace System.Collections.Generic
+{
+ // Provides the Create factory method for KeyValuePair<TKey, TValue>.
+ public static class KeyValuePair
+ {
+ // Creates a new KeyValuePair<TKey, TValue> from the given values.
+ public static KeyValuePair<TKey, TValue> Create<TKey, TValue>(TKey key, TValue value)
+ {
+ return new KeyValuePair<TKey, TValue>(key, value);
+ }
+
+ /// <summary>
+ /// Used by KeyValuePair.ToString to reduce generic code
+ /// </summary>
+ internal static string PairToString(object key, object value)
+ {
+ StringBuilder s = StringBuilderCache.Acquire();
+ s.Append('[');
+
+ if (key != null)
+ {
+ s.Append(key);
+ }
+
+ s.Append(", ");
+
+ if (value != null)
+ {
+ s.Append(value);
+ }
+
+ s.Append(']');
+
+ return StringBuilderCache.GetStringAndRelease(s);
+ }
+ }
+
+ // A KeyValuePair holds a key and a value from a dictionary.
+ // It is used by the IEnumerable<T> implementation for both IDictionary<TKey, TValue>
+ // and IReadOnlyDictionary<TKey, TValue>.
+ [Serializable]
+ public struct KeyValuePair<TKey, TValue>
+ {
+ private TKey key; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload.
+ private TValue value; // DO NOT change the field name, it's required for compatibility with desktop .NET as it appears in serialization payload.
+
+ public KeyValuePair(TKey key, TValue value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+
+ public TKey Key
+ {
+ get { return key; }
+ }
+
+ public TValue Value
+ {
+ get { return value; }
+ }
+
+ public override string ToString()
+ {
+ return KeyValuePair.PairToString(Key, Value);
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void Deconstruct(out TKey key, out TValue value)
+ {
+ key = Key;
+ value = Value;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/ICollection.cs b/src/mscorlib/shared/System/Collections/ICollection.cs
new file mode 100644
index 0000000000..80ea092363
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/ICollection.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Collections
+{
+ // Base interface for all collections, defining enumerators, size, and
+ // synchronization methods.
+ public interface ICollection : IEnumerable
+ {
+ // Interfaces are not serialable
+ // CopyTo copies a collection into an Array, starting at a particular
+ // index into the array.
+ //
+ void CopyTo(Array array, int index);
+
+ // Number of items in the collections.
+ int Count
+ { get; }
+
+
+ // SyncRoot will return an Object to use for synchronization
+ // (thread safety). You can use this object in your code to take a
+ // lock on the collection, even if this collection is a wrapper around
+ // another collection. The intent is to tunnel through to a real
+ // implementation of a collection, and use one of the internal objects
+ // found in that code.
+ //
+ // In the absense of a static Synchronized method on a collection,
+ // the expected usage for SyncRoot would look like this:
+ //
+ // ICollection col = ...
+ // lock (col.SyncRoot) {
+ // // Some operation on the collection, which is now thread safe.
+ // // This may include multiple operations.
+ // }
+ //
+ //
+ // The system-provided collections have a static method called
+ // Synchronized which will create a thread-safe wrapper around the
+ // collection. All access to the collection that you want to be
+ // thread-safe should go through that wrapper collection. However, if
+ // you need to do multiple calls on that collection (such as retrieving
+ // two items, or checking the count then doing something), you should
+ // NOT use our thread-safe wrapper since it only takes a lock for the
+ // duration of a single method call. Instead, use Monitor.Enter/Exit
+ // or your language's equivalent to the C# lock keyword as mentioned
+ // above.
+ //
+ // For collections with no publically available underlying store, the
+ // expected implementation is to simply return the this pointer. Note
+ // that the this pointer may not be sufficient for collections that
+ // wrap other collections; those should return the underlying
+ // collection's SyncRoot property.
+ Object SyncRoot
+ { get; }
+
+ // Is this collection synchronized (i.e., thread-safe)? If you want a
+ // thread-safe collection, you can use SyncRoot as an object to
+ // synchronize your collection with. If you're using one of the
+ // collections in System.Collections, you could call the static
+ // Synchronized method to get a thread-safe wrapper around the
+ // underlying collection.
+ bool IsSynchronized
+ { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/IComparer.cs b/src/mscorlib/shared/System/Collections/IComparer.cs
new file mode 100644
index 0000000000..cef91c3ffa
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/IComparer.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.Collections
+{
+ // The IComparer interface implements a method that compares two objects. It is
+ // used in conjunction with the Sort and BinarySearch methods on
+ // the Array and List classes.
+ //
+ // Interfaces are not serializable
+ public interface IComparer
+ {
+ // Compares two objects. An implementation of this method must return a
+ // value less than zero if x is less than y, zero if x is equal to y, or a
+ // value greater than zero if x is greater than y.
+ //
+ int Compare(Object x, Object y);
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/IDictionary.cs b/src/mscorlib/shared/System/Collections/IDictionary.cs
new file mode 100644
index 0000000000..8bc7fcf125
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/IDictionary.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Collections
+{
+ // An IDictionary is a possibly unordered set of key-value pairs.
+ // Keys can be any non-null object. Values can be any object.
+ // You can look up a value in an IDictionary via the default indexed
+ // property, Items.
+ public interface IDictionary : ICollection
+ {
+ // Interfaces are not serializable
+ // The Item property provides methods to read and edit entries
+ // in the Dictionary.
+ Object this[Object key]
+ {
+ get;
+ set;
+ }
+
+ // Returns a collections of the keys in this dictionary.
+ ICollection Keys
+ {
+ get;
+ }
+
+ // Returns a collections of the values in this dictionary.
+ ICollection Values
+ {
+ get;
+ }
+
+ // Returns whether this dictionary contains a particular key.
+ //
+ bool Contains(Object key);
+
+ // Adds a key-value pair to the dictionary.
+ //
+ void Add(Object key, Object value);
+
+ // Removes all pairs from the dictionary.
+ void Clear();
+
+ bool IsReadOnly
+ { get; }
+
+ bool IsFixedSize
+ { get; }
+
+ // Returns an IDictionaryEnumerator for this dictionary.
+ new IDictionaryEnumerator GetEnumerator();
+
+ // Removes a particular key from the dictionary.
+ //
+ void Remove(Object key);
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/IDictionaryEnumerator.cs b/src/mscorlib/shared/System/Collections/IDictionaryEnumerator.cs
new file mode 100644
index 0000000000..451cf68976
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/IDictionaryEnumerator.cs
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Collections
+{
+ // This interface represents an enumerator that allows sequential access to the
+ // elements of a dictionary. Upon creation, an enumerator is conceptually
+ // positioned before the first element of the enumeration. The first call to the
+ // MoveNext method brings the first element of the enumeration into view,
+ // and each successive call to MoveNext brings the next element into
+ // view until MoveNext returns false, indicating that there are no more
+ // elements to enumerate. Following each call to MoveNext, the
+ // Key and Value methods are used to obtain the key and
+ // value of the element currently in view. The values returned by calls to
+ // Key and Value are undefined before the first call to
+ // MoveNext and following a call to MoveNext that returned false.
+ // Enumerators are typically used in while loops of the form
+ //
+ // IDictionaryEnumerator e = ...;
+ // while (e.MoveNext()) {
+ // Object key = e.Key;
+ // Object value = e.Value;
+ // ...
+ // }
+ //
+ // The IDictionaryEnumerator interface extends the IEnumerator
+ // inerface and can thus be used as a regular enumerator. The Current
+ // method of an IDictionaryEnumerator returns a DictionaryEntry containing
+ // the current key and value pair. However, the GetEntry method will
+ // return the same DictionaryEntry and avoids boxing the DictionaryEntry (boxing
+ // is somewhat expensive).
+ //
+ public interface IDictionaryEnumerator : IEnumerator
+ {
+ // Returns the key of the current element of the enumeration. The returned
+ // value is undefined before the first call to GetNext and following
+ // a call to GetNext that returned false. Multiple calls to
+ // GetKey with no intervening calls to GetNext will return
+ // the same object.
+ //
+ Object Key
+ {
+ get;
+ }
+
+ // Returns the value of the current element of the enumeration. The
+ // returned value is undefined before the first call to GetNext and
+ // following a call to GetNext that returned false. Multiple calls
+ // to GetValue with no intervening calls to GetNext will
+ // return the same object.
+ //
+ Object Value
+ {
+ get;
+ }
+
+ // GetBlock will copy dictionary values into the given Array. It will either
+ // fill up the array, or if there aren't enough elements, it will
+ // copy as much as possible into the Array. The number of elements
+ // copied is returned.
+ //
+ DictionaryEntry Entry
+ {
+ get;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/IEnumerable.cs b/src/mscorlib/shared/System/Collections/IEnumerable.cs
new file mode 100644
index 0000000000..91aec62423
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/IEnumerable.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+
+namespace System.Collections
+{
+ public interface IEnumerable
+ {
+ // Returns an IEnumerator for this enumerable Object. The enumerator provides
+ // a simple way to access all the contents of a collection.
+ [Pure]
+ IEnumerator GetEnumerator();
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/IEnumerator.cs b/src/mscorlib/shared/System/Collections/IEnumerator.cs
new file mode 100644
index 0000000000..2c2c2e4a97
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/IEnumerator.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.InteropServices;
+
+namespace System.Collections
+{
+ // Base interface for all enumerators, providing a simple approach
+ // to iterating over a collection.
+ public interface IEnumerator
+ {
+ // Advances the enumerator to the next element of the enumeration and
+ // returns a boolean indicating whether an element is available. Upon
+ // creation, an enumerator is conceptually positioned before the first
+ // element of the enumeration, and the first call to MoveNext
+ // brings the first element of the enumeration into view.
+ //
+ bool MoveNext();
+
+ // Returns the current element of the enumeration. The returned value is
+ // undefined before the first call to MoveNext and following a
+ // call to MoveNext that returned false. Multiple calls to
+ // GetCurrent with no intervening calls to MoveNext
+ // will return the same object.
+ //
+ Object Current
+ {
+ get;
+ }
+
+ // Resets the enumerator to the beginning of the enumeration, starting over.
+ // The preferred behavior for Reset is to return the exact same enumeration.
+ // This means if you modify the underlying collection then call Reset, your
+ // IEnumerator will be invalid, just as it would have been if you had called
+ // MoveNext or Current.
+ //
+ void Reset();
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/IEqualityComparer.cs b/src/mscorlib/shared/System/Collections/IEqualityComparer.cs
new file mode 100644
index 0000000000..35513f577d
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/IEqualityComparer.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.Collections
+{
+ // An IEqualityComparer is a mechanism to consume custom performant comparison infrastructure
+ // that can be consumed by some of the common collections.
+ public interface IEqualityComparer
+ {
+ bool Equals(Object x, Object y);
+ int GetHashCode(Object obj);
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/IList.cs b/src/mscorlib/shared/System/Collections/IList.cs
new file mode 100644
index 0000000000..bb2e221cc1
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/IList.cs
@@ -0,0 +1,60 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Collections
+{
+ // An IList is an ordered collection of objects. The exact ordering
+ // is up to the implementation of the list, ranging from a sorted
+ // order to insertion order.
+ public interface IList : ICollection
+ {
+ // The Item property provides methods to read and edit entries in the List.
+ Object this[int index]
+ {
+ get;
+ set;
+ }
+
+ // Adds an item to the list. The exact position in the list is
+ // implementation-dependent, so while ArrayList may always insert
+ // in the last available location, a SortedList most likely would not.
+ // The return value is the position the new element was inserted in.
+ int Add(Object value);
+
+ // Returns whether the list contains a particular item.
+ bool Contains(Object value);
+
+ // Removes all items from the list.
+ void Clear();
+
+ bool IsReadOnly
+ { get; }
+
+
+ bool IsFixedSize
+ {
+ get;
+ }
+
+
+ // Returns the index of a particular item, if it is in the list.
+ // Returns -1 if the item isn't in the list.
+ int IndexOf(Object value);
+
+ // Inserts value into the list at position index.
+ // index must be non-negative and less than or equal to the
+ // number of elements in the list. If index equals the number
+ // of items in the list, then value is appended to the end.
+ void Insert(int index, Object value);
+
+ // Removes an item from the list.
+ void Remove(Object value);
+
+ // Removes the item at position index.
+ void RemoveAt(int index);
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/IStructuralComparable.cs b/src/mscorlib/shared/System/Collections/IStructuralComparable.cs
new file mode 100644
index 0000000000..a5e4834b9b
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/IStructuralComparable.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.Collections
+{
+ public interface IStructuralComparable
+ {
+ Int32 CompareTo(Object other, IComparer comparer);
+ }
+}
diff --git a/src/mscorlib/shared/System/Collections/IStructuralEquatable.cs b/src/mscorlib/shared/System/Collections/IStructuralEquatable.cs
new file mode 100644
index 0000000000..4e61d5e75f
--- /dev/null
+++ b/src/mscorlib/shared/System/Collections/IStructuralEquatable.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Collections
+{
+ public interface IStructuralEquatable
+ {
+ Boolean Equals(Object other, IEqualityComparer comparer);
+ int GetHashCode(IEqualityComparer comparer);
+ }
+}
diff --git a/src/mscorlib/shared/System/ComponentModel/DefaultValueAttribute.cs b/src/mscorlib/shared/System/ComponentModel/DefaultValueAttribute.cs
new file mode 100644
index 0000000000..3cdc907297
--- /dev/null
+++ b/src/mscorlib/shared/System/ComponentModel/DefaultValueAttribute.cs
@@ -0,0 +1,228 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.ComponentModel;
+using System.Diagnostics;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace System.ComponentModel
+{
+ /// <devdoc>
+ /// <para>Specifies the default value for a property.</para>
+ /// </devdoc>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")]
+ [AttributeUsage(AttributeTargets.All)]
+ public class DefaultValueAttribute : Attribute
+ {
+ /// <devdoc>
+ /// This is the default value.
+ /// </devdoc>
+ private object _value;
+
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class, converting the
+ /// specified value to the
+ /// specified type, and using the U.S. English culture as the
+ /// translation
+ /// context.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(Type type, string value)
+ {
+ // The try/catch here is because attributes should never throw exceptions. We would fail to
+ // load an otherwise normal class.
+ try
+ {
+ if (type.IsSubclassOf(typeof(Enum)))
+ {
+ _value = Enum.Parse(type, value, true);
+ }
+ else if (type == typeof(TimeSpan))
+ {
+ _value = TimeSpan.Parse(value);
+ }
+ else
+ {
+ _value = Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a Unicode
+ /// character.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(char value)
+ {
+ _value = value;
+ }
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using an 8-bit unsigned
+ /// integer.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(byte value)
+ {
+ _value = value;
+ }
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a 16-bit signed
+ /// integer.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(short value)
+ {
+ _value = value;
+ }
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a 32-bit signed
+ /// integer.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(int value)
+ {
+ _value = value;
+ }
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a 64-bit signed
+ /// integer.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(long value)
+ {
+ _value = value;
+ }
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a
+ /// single-precision floating point
+ /// number.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(float value)
+ {
+ _value = value;
+ }
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a
+ /// double-precision floating point
+ /// number.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(double value)
+ {
+ _value = value;
+ }
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.Boolean'/>
+ /// value.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(bool value)
+ {
+ _value = value;
+ }
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.String'/>.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(string value)
+ {
+ _value = value;
+ }
+
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/>
+ /// class.</para>
+ /// </devdoc>
+ public DefaultValueAttribute(object value)
+ {
+ _value = value;
+ }
+
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.SByte'/>
+ /// value.</para>
+ /// </devdoc>
+ [CLSCompliant(false)]
+ public DefaultValueAttribute(sbyte value)
+ {
+ _value = value;
+ }
+
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.UInt16'/>
+ /// value.</para>
+ /// </devdoc>
+ [CLSCompliant(false)]
+ public DefaultValueAttribute(ushort value)
+ {
+ _value = value;
+ }
+
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.UInt32'/>
+ /// value.</para>
+ /// </devdoc>
+ [CLSCompliant(false)]
+ public DefaultValueAttribute(uint value)
+ {
+ _value = value;
+ }
+
+ /// <devdoc>
+ /// <para>Initializes a new instance of the <see cref='System.ComponentModel.DefaultValueAttribute'/> class using a <see cref='System.UInt64'/>
+ /// value.</para>
+ /// </devdoc>
+ [CLSCompliant(false)]
+ public DefaultValueAttribute(ulong value)
+ {
+ _value = value;
+ }
+
+ /// <devdoc>
+ /// <para>
+ /// Gets the default value of the property this
+ /// attribute is
+ /// bound to.
+ /// </para>
+ /// </devdoc>
+ public virtual object Value
+ {
+ get
+ {
+ return _value;
+ }
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ DefaultValueAttribute other = obj as DefaultValueAttribute;
+
+ if (other != null)
+ {
+ if (Value != null)
+ {
+ return Value.Equals(other.Value);
+ }
+ else
+ {
+ return (other.Value == null);
+ }
+ }
+ return false;
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+
+ protected void SetValue(object value)
+ {
+ _value = value;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/ComponentModel/EditorBrowsableAttribute.cs b/src/mscorlib/shared/System/ComponentModel/EditorBrowsableAttribute.cs
new file mode 100644
index 0000000000..9b4d6e626e
--- /dev/null
+++ b/src/mscorlib/shared/System/ComponentModel/EditorBrowsableAttribute.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.ComponentModel
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Delegate | AttributeTargets.Interface)]
+ public sealed class EditorBrowsableAttribute : Attribute
+ {
+ private EditorBrowsableState browsableState;
+
+ public EditorBrowsableAttribute(EditorBrowsableState state)
+ {
+ browsableState = state;
+ }
+
+ public EditorBrowsableAttribute() : this(EditorBrowsableState.Always) { }
+
+ public EditorBrowsableState State
+ {
+ get { return browsableState; }
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj == this)
+ {
+ return true;
+ }
+
+ EditorBrowsableAttribute other = obj as EditorBrowsableAttribute;
+
+ return (other != null) && other.browsableState == browsableState;
+ }
+
+ public override int GetHashCode()
+ {
+ return base.GetHashCode();
+ }
+ }
+
+ public enum EditorBrowsableState
+ {
+ Always,
+ Never,
+ Advanced
+ }
+}
diff --git a/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyHashAlgorithm.cs b/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyHashAlgorithm.cs
new file mode 100644
index 0000000000..aca8da5932
--- /dev/null
+++ b/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyHashAlgorithm.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Configuration.Assemblies
+{
+ public enum AssemblyHashAlgorithm
+ {
+ None = 0,
+ MD5 = 0x8003,
+ SHA1 = 0x8004,
+ SHA256 = 0x800c,
+ SHA384 = 0x800d,
+ SHA512 = 0x800e,
+ }
+}
diff --git a/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyVersionCompatibility.cs b/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyVersionCompatibility.cs
new file mode 100644
index 0000000000..ef7b3eb45f
--- /dev/null
+++ b/src/mscorlib/shared/System/Configuration/Assemblies/AssemblyVersionCompatibility.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Configuration.Assemblies
+{
+ public enum AssemblyVersionCompatibility
+ {
+ SameMachine = 1,
+ SameProcess = 2,
+ SameDomain = 3,
+ }
+}
diff --git a/src/mscorlib/shared/System/Convert.cs b/src/mscorlib/shared/System/Convert.cs
new file mode 100644
index 0000000000..576f78f1f1
--- /dev/null
+++ b/src/mscorlib/shared/System/Convert.cs
@@ -0,0 +1,3031 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Globalization;
+using System.Threading;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+using System.Security;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+
+namespace System
+{
+ [Flags]
+ public enum Base64FormattingOptions
+ {
+ None = 0,
+ InsertLineBreaks = 1
+ }
+
+ // Returns the type code of this object. An implementation of this method
+ // must not return TypeCode.Empty (which represents a null reference) or
+ // TypeCode.Object (which represents an object that doesn't implement the
+ // IConvertible interface). An implementation of this method should return
+ // TypeCode.DBNull if the value of this object is a database null. For
+ // example, a nullable integer type should return TypeCode.DBNull if the
+ // value of the object is the database null. Otherwise, an implementation
+ // of this method should return the TypeCode that best describes the
+ // internal representation of the object.
+ // The Value class provides conversion and querying methods for values. The
+ // Value class contains static members only, and it is not possible to create
+ // instances of the class.
+ //
+ // The statically typed conversion methods provided by the Value class are all
+ // of the form:
+ //
+ // public static XXX ToXXX(YYY value)
+ //
+ // where XXX is the target type and YYY is the source type. The matrix below
+ // shows the set of supported conversions. The set of conversions is symmetric
+ // such that for every ToXXX(YYY) there is also a ToYYY(XXX).
+ //
+ // From: To: Bol Chr SBy Byt I16 U16 I32 U32 I64 U64 Sgl Dbl Dec Dat Str
+ // ----------------------------------------------------------------------
+ // Boolean x x x x x x x x x x x x x
+ // Char x x x x x x x x x x
+ // SByte x x x x x x x x x x x x x x
+ // Byte x x x x x x x x x x x x x x
+ // Int16 x x x x x x x x x x x x x x
+ // UInt16 x x x x x x x x x x x x x x
+ // Int32 x x x x x x x x x x x x x x
+ // UInt32 x x x x x x x x x x x x x x
+ // Int64 x x x x x x x x x x x x x x
+ // UInt64 x x x x x x x x x x x x x x
+ // Single x x x x x x x x x x x x x
+ // Double x x x x x x x x x x x x x
+ // Decimal x x x x x x x x x x x x x
+ // DateTime x x
+ // String x x x x x x x x x x x x x x x
+ // ----------------------------------------------------------------------
+ //
+ // For dynamic conversions, the Value class provides a set of methods of the
+ // form:
+ //
+ // public static XXX ToXXX(object value)
+ //
+ // where XXX is the target type (Boolean, Char, SByte, Byte, Int16, UInt16,
+ // Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime,
+ // or String). The implementations of these methods all take the form:
+ //
+ // public static XXX toXXX(object value) {
+ // return value == null? XXX.Default: ((IConvertible)value).ToXXX();
+ // }
+ //
+ // The code first checks if the given value is a null reference (which is the
+ // same as Value.Empty), in which case it returns the default value for type
+ // XXX. Otherwise, a cast to IConvertible is performed, and the appropriate ToXXX()
+ // method is invoked on the object. An InvalidCastException is thrown if the
+ // cast to IConvertible fails, and that exception is simply allowed to propagate out
+ // of the conversion method.
+
+ // Constant representing the database null value. This value is used in
+ // database applications to indicate the absense of a known value. Note
+ // that Value.DBNull is NOT the same as a null object reference, which is
+ // represented by Value.Empty.
+ //
+ // The Equals() method of DBNull always returns false, even when the
+ // argument is itself DBNull.
+ //
+ // When passed Value.DBNull, the Value.GetTypeCode() method returns
+ // TypeCode.DBNull.
+ //
+ // When passed Value.DBNull, the Value.ToXXX() methods all throw an
+ // InvalidCastException.
+
+ public static class Convert
+ {
+ //A typeof operation is fairly expensive (does a system call), so we'll cache these here
+ //statically. These are exactly lined up with the TypeCode, eg. ConvertType[TypeCode.Int16]
+ //will give you the type of an Int16.
+ internal static readonly Type[] ConvertTypes = {
+ typeof(System.Empty),
+ typeof(Object),
+ typeof(System.DBNull),
+ typeof(Boolean),
+ typeof(Char),
+ typeof(SByte),
+ typeof(Byte),
+ typeof(Int16),
+ typeof(UInt16),
+ typeof(Int32),
+ typeof(UInt32),
+ typeof(Int64),
+ typeof(UInt64),
+ typeof(Single),
+ typeof(Double),
+ typeof(Decimal),
+ typeof(DateTime),
+ typeof(Object), //TypeCode is discontinuous so we need a placeholder.
+ typeof(String)
+ };
+
+ // Need to special case Enum because typecode will be underlying type, e.g. Int32
+ private static readonly Type EnumType = typeof(Enum);
+
+ internal static readonly char[] base64Table = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
+ 'P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d',
+ 'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
+ 't','u','v','w','x','y','z','0','1','2','3','4','5','6','7',
+ '8','9','+','/','=' };
+
+ private const Int32 base64LineBreakPosition = 76;
+
+#if _DEBUG
+ private static bool TriggerAsserts = DoAsserts();
+ private static bool DoAsserts()
+ {
+ Debug.Assert(ConvertTypes != null, "[Convert.cctor]ConvertTypes!=null");
+ Debug.Assert(ConvertTypes.Length == ((int)TypeCode.String + 1), "[Convert.cctor]ConvertTypes.Length == ((int)TypeCode.String + 1)");
+ Debug.Assert(ConvertTypes[(int)TypeCode.Empty] == typeof(System.Empty),
+ "[Convert.cctor]ConvertTypes[(int)TypeCode.Empty]==typeof(System.Empty)");
+ Debug.Assert(ConvertTypes[(int)TypeCode.String] == typeof(String),
+ "[Convert.cctor]ConvertTypes[(int)TypeCode.String]==typeof(System.String)");
+ Debug.Assert(ConvertTypes[(int)TypeCode.Int32] == typeof(int),
+ "[Convert.cctor]ConvertTypes[(int)TypeCode.Int32]==typeof(int)");
+ return true;
+ }
+#endif
+
+ public static readonly Object DBNull = System.DBNull.Value;
+
+ // Returns the type code for the given object. If the argument is null,
+ // the result is TypeCode.Empty. If the argument is not a value (i.e. if
+ // the object does not implement IConvertible), the result is TypeCode.Object.
+ // Otherwise, the result is the type code of the object, as determined by
+ // the object's implementation of IConvertible.
+ [Pure]
+ public static TypeCode GetTypeCode(object value)
+ {
+ if (value == null) return TypeCode.Empty;
+ IConvertible temp = value as IConvertible;
+ if (temp != null)
+ {
+ return temp.GetTypeCode();
+ }
+ return TypeCode.Object;
+ }
+
+ // Returns true if the given object is a database null. This operation
+ // corresponds to "value.GetTypeCode() == TypeCode.DBNull".
+ [Pure]
+ public static bool IsDBNull(object value)
+ {
+ if (value == System.DBNull.Value) return true;
+ IConvertible convertible = value as IConvertible;
+ return convertible != null ? convertible.GetTypeCode() == TypeCode.DBNull : false;
+ }
+
+ // Converts the given object to the given type. In general, this method is
+ // equivalent to calling the Value.ToXXX(value) method for the given
+ // typeCode and boxing the result.
+ //
+ // The method first checks if the given object implements IConvertible. If not,
+ // the only permitted conversion is from a null to TypeCode.Empty, the
+ // result of which is null.
+ //
+ // If the object does implement IConvertible, a check is made to see if the
+ // object already has the given type code, in which case the object is
+ // simply returned. Otherwise, the appropriate ToXXX() is invoked on the
+ // object's implementation of IConvertible.
+ public static Object ChangeType(Object value, TypeCode typeCode)
+ {
+ return ChangeType(value, typeCode, CultureInfo.CurrentCulture);
+ }
+
+ public static Object ChangeType(Object value, TypeCode typeCode, IFormatProvider provider)
+ {
+ if (value == null && (typeCode == TypeCode.Empty || typeCode == TypeCode.String || typeCode == TypeCode.Object))
+ {
+ return null;
+ }
+
+ IConvertible v = value as IConvertible;
+ if (v == null)
+ {
+ throw new InvalidCastException(SR.InvalidCast_IConvertible);
+ }
+
+ // This line is invalid for things like Enums that return a TypeCode
+ // of Int32, but the object can't actually be cast to an Int32.
+ // if (v.GetTypeCode() == typeCode) return value;
+ switch (typeCode)
+ {
+ case TypeCode.Boolean:
+ return v.ToBoolean(provider);
+ case TypeCode.Char:
+ return v.ToChar(provider);
+ case TypeCode.SByte:
+ return v.ToSByte(provider);
+ case TypeCode.Byte:
+ return v.ToByte(provider);
+ case TypeCode.Int16:
+ return v.ToInt16(provider);
+ case TypeCode.UInt16:
+ return v.ToUInt16(provider);
+ case TypeCode.Int32:
+ return v.ToInt32(provider);
+ case TypeCode.UInt32:
+ return v.ToUInt32(provider);
+ case TypeCode.Int64:
+ return v.ToInt64(provider);
+ case TypeCode.UInt64:
+ return v.ToUInt64(provider);
+ case TypeCode.Single:
+ return v.ToSingle(provider);
+ case TypeCode.Double:
+ return v.ToDouble(provider);
+ case TypeCode.Decimal:
+ return v.ToDecimal(provider);
+ case TypeCode.DateTime:
+ return v.ToDateTime(provider);
+ case TypeCode.String:
+ return v.ToString(provider);
+ case TypeCode.Object:
+ return value;
+ case TypeCode.DBNull:
+ throw new InvalidCastException(SR.InvalidCast_DBNull);
+ case TypeCode.Empty:
+ throw new InvalidCastException(SR.InvalidCast_Empty);
+ default:
+ throw new ArgumentException(SR.Arg_UnknownTypeCode);
+ }
+ }
+
+ internal static Object DefaultToType(IConvertible value, Type targetType, IFormatProvider provider)
+ {
+ Debug.Assert(value != null, "[Convert.DefaultToType]value!=null");
+ if (targetType == null)
+ {
+ throw new ArgumentNullException(nameof(targetType));
+ }
+ Contract.EndContractBlock();
+
+ if (ReferenceEquals(value.GetType(), targetType))
+ {
+ return value;
+ }
+
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Boolean]))
+ return value.ToBoolean(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Char]))
+ return value.ToChar(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.SByte]))
+ return value.ToSByte(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Byte]))
+ return value.ToByte(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Int16]))
+ return value.ToInt16(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.UInt16]))
+ return value.ToUInt16(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Int32]))
+ return value.ToInt32(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.UInt32]))
+ return value.ToUInt32(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Int64]))
+ return value.ToInt64(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.UInt64]))
+ return value.ToUInt64(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Single]))
+ return value.ToSingle(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Double]))
+ return value.ToDouble(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Decimal]))
+ return value.ToDecimal(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.DateTime]))
+ return value.ToDateTime(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.String]))
+ return value.ToString(provider);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Object]))
+ return (Object)value;
+ // Need to special case Enum because typecode will be underlying type, e.g. Int32
+ if (ReferenceEquals(targetType, EnumType))
+ return (Enum)value;
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.DBNull]))
+ throw new InvalidCastException(SR.InvalidCast_DBNull);
+ if (ReferenceEquals(targetType, ConvertTypes[(int)TypeCode.Empty]))
+ throw new InvalidCastException(SR.InvalidCast_Empty);
+
+ throw new InvalidCastException(string.Format(SR.InvalidCast_FromTo, value.GetType().FullName, targetType.FullName));
+ }
+
+ public static Object ChangeType(Object value, Type conversionType)
+ {
+ return ChangeType(value, conversionType, CultureInfo.CurrentCulture);
+ }
+
+ public static Object ChangeType(Object value, Type conversionType, IFormatProvider provider)
+ {
+ if (ReferenceEquals(conversionType, null))
+ {
+ throw new ArgumentNullException(nameof(conversionType));
+ }
+ Contract.EndContractBlock();
+
+ if (value == null)
+ {
+ if (conversionType.IsValueType)
+ {
+ throw new InvalidCastException(SR.InvalidCast_CannotCastNullToValueType);
+ }
+ return null;
+ }
+
+ IConvertible ic = value as IConvertible;
+ if (ic == null)
+ {
+ if (value.GetType() == conversionType)
+ {
+ return value;
+ }
+ throw new InvalidCastException(SR.InvalidCast_IConvertible);
+ }
+
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Boolean]))
+ return ic.ToBoolean(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Char]))
+ return ic.ToChar(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.SByte]))
+ return ic.ToSByte(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Byte]))
+ return ic.ToByte(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Int16]))
+ return ic.ToInt16(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.UInt16]))
+ return ic.ToUInt16(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Int32]))
+ return ic.ToInt32(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.UInt32]))
+ return ic.ToUInt32(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Int64]))
+ return ic.ToInt64(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.UInt64]))
+ return ic.ToUInt64(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Single]))
+ return ic.ToSingle(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Double]))
+ return ic.ToDouble(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Decimal]))
+ return ic.ToDecimal(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.DateTime]))
+ return ic.ToDateTime(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.String]))
+ return ic.ToString(provider);
+ if (ReferenceEquals(conversionType, ConvertTypes[(int)TypeCode.Object]))
+ return (Object)value;
+
+ return ic.ToType(conversionType, provider);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowCharOverflowException() { throw new OverflowException(SR.Overflow_Char); }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowByteOverflowException() { throw new OverflowException(SR.Overflow_Byte); }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowSByteOverflowException() { throw new OverflowException(SR.Overflow_SByte); }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowInt16OverflowException() { throw new OverflowException(SR.Overflow_Int16); }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowUInt16OverflowException() { throw new OverflowException(SR.Overflow_UInt16); }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowInt32OverflowException() { throw new OverflowException(SR.Overflow_Int32); }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowUInt32OverflowException() { throw new OverflowException(SR.Overflow_UInt32); }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowInt64OverflowException() { throw new OverflowException(SR.Overflow_Int64); }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void ThrowUInt64OverflowException() { throw new OverflowException(SR.Overflow_UInt64); }
+
+ // Conversions to Boolean
+ public static bool ToBoolean(Object value)
+ {
+ return value == null ? false : ((IConvertible)value).ToBoolean(null);
+ }
+
+ public static bool ToBoolean(Object value, IFormatProvider provider)
+ {
+ return value == null ? false : ((IConvertible)value).ToBoolean(provider);
+ }
+
+
+ public static bool ToBoolean(bool value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static bool ToBoolean(sbyte value)
+ {
+ return value != 0;
+ }
+
+ // To be consistent with IConvertible in the base data types else we get different semantics
+ // with widening operations. Without this operator this widen succeeds,with this API the widening throws.
+ public static bool ToBoolean(char value)
+ {
+ return ((IConvertible)value).ToBoolean(null);
+ }
+
+ public static bool ToBoolean(byte value)
+ {
+ return value != 0;
+ }
+
+
+ public static bool ToBoolean(short value)
+ {
+ return value != 0;
+ }
+
+ [CLSCompliant(false)]
+ public static bool ToBoolean(ushort value)
+ {
+ return value != 0;
+ }
+
+ public static bool ToBoolean(int value)
+ {
+ return value != 0;
+ }
+
+ [CLSCompliant(false)]
+ public static bool ToBoolean(uint value)
+ {
+ return value != 0;
+ }
+
+ public static bool ToBoolean(long value)
+ {
+ return value != 0;
+ }
+
+ [CLSCompliant(false)]
+ public static bool ToBoolean(ulong value)
+ {
+ return value != 0;
+ }
+
+ public static bool ToBoolean(String value)
+ {
+ if (value == null)
+ return false;
+ return Boolean.Parse(value);
+ }
+
+ public static bool ToBoolean(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return false;
+ return Boolean.Parse(value);
+ }
+
+ public static bool ToBoolean(float value)
+ {
+ return value != 0;
+ }
+
+ public static bool ToBoolean(double value)
+ {
+ return value != 0;
+ }
+
+ public static bool ToBoolean(decimal value)
+ {
+ return value != 0;
+ }
+
+ public static bool ToBoolean(DateTime value)
+ {
+ return ((IConvertible)value).ToBoolean(null);
+ }
+
+ // Disallowed conversions to Boolean
+ // public static bool ToBoolean(TimeSpan value)
+
+ // Conversions to Char
+
+
+ public static char ToChar(object value)
+ {
+ return value == null ? (char)0 : ((IConvertible)value).ToChar(null);
+ }
+
+ public static char ToChar(object value, IFormatProvider provider)
+ {
+ return value == null ? (char)0 : ((IConvertible)value).ToChar(provider);
+ }
+
+ public static char ToChar(bool value)
+ {
+ return ((IConvertible)value).ToChar(null);
+ }
+
+ public static char ToChar(char value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static char ToChar(sbyte value)
+ {
+ if (value < 0) ThrowCharOverflowException();
+ Contract.EndContractBlock();
+ return (char)value;
+ }
+
+ public static char ToChar(byte value)
+ {
+ return (char)value;
+ }
+
+ public static char ToChar(short value)
+ {
+ if (value < 0) ThrowCharOverflowException();
+ Contract.EndContractBlock();
+ return (char)value;
+ }
+
+ [CLSCompliant(false)]
+ public static char ToChar(ushort value)
+ {
+ return (char)value;
+ }
+
+ public static char ToChar(int value)
+ {
+ if (value < 0 || value > Char.MaxValue) ThrowCharOverflowException();
+ Contract.EndContractBlock();
+ return (char)value;
+ }
+
+ [CLSCompliant(false)]
+ public static char ToChar(uint value)
+ {
+ if (value > Char.MaxValue) ThrowCharOverflowException();
+ Contract.EndContractBlock();
+ return (char)value;
+ }
+
+ public static char ToChar(long value)
+ {
+ if (value < 0 || value > Char.MaxValue) ThrowCharOverflowException();
+ Contract.EndContractBlock();
+ return (char)value;
+ }
+
+ [CLSCompliant(false)]
+ public static char ToChar(ulong value)
+ {
+ if (value > Char.MaxValue) ThrowCharOverflowException();
+ Contract.EndContractBlock();
+ return (char)value;
+ }
+
+ //
+ // @VariantSwitch
+ // Remove FormatExceptions;
+ //
+ public static char ToChar(String value)
+ {
+ return ToChar(value, null);
+ }
+
+ public static char ToChar(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+ Contract.EndContractBlock();
+
+ if (value.Length != 1)
+ throw new FormatException(SR.Format_NeedSingleChar);
+
+ return value[0];
+ }
+
+ // To be consistent with IConvertible in the base data types else we get different semantics
+ // with widening operations. Without this operator this widen succeeds,with this API the widening throws.
+ public static char ToChar(float value)
+ {
+ return ((IConvertible)value).ToChar(null);
+ }
+
+ // To be consistent with IConvertible in the base data types else we get different semantics
+ // with widening operations. Without this operator this widen succeeds,with this API the widening throws.
+ public static char ToChar(double value)
+ {
+ return ((IConvertible)value).ToChar(null);
+ }
+
+ // To be consistent with IConvertible in the base data types else we get different semantics
+ // with widening operations. Without this operator this widen succeeds,with this API the widening throws.
+ public static char ToChar(decimal value)
+ {
+ return ((IConvertible)value).ToChar(null);
+ }
+
+ public static char ToChar(DateTime value)
+ {
+ return ((IConvertible)value).ToChar(null);
+ }
+
+
+ // Disallowed conversions to Char
+ // public static char ToChar(TimeSpan value)
+
+ // Conversions to SByte
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(object value)
+ {
+ return value == null ? (sbyte)0 : ((IConvertible)value).ToSByte(null);
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(object value, IFormatProvider provider)
+ {
+ return value == null ? (sbyte)0 : ((IConvertible)value).ToSByte(provider);
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(bool value)
+ {
+ return value ? (sbyte)Boolean.True : (sbyte)Boolean.False;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(sbyte value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(char value)
+ {
+ if (value > SByte.MaxValue) ThrowSByteOverflowException();
+ Contract.EndContractBlock();
+ return (sbyte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(byte value)
+ {
+ if (value > SByte.MaxValue) ThrowSByteOverflowException();
+ Contract.EndContractBlock();
+ return (sbyte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(short value)
+ {
+ if (value < SByte.MinValue || value > SByte.MaxValue) ThrowSByteOverflowException();
+ Contract.EndContractBlock();
+ return (sbyte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(ushort value)
+ {
+ if (value > SByte.MaxValue) ThrowSByteOverflowException();
+ Contract.EndContractBlock();
+ return (sbyte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(int value)
+ {
+ if (value < SByte.MinValue || value > SByte.MaxValue) ThrowSByteOverflowException();
+ Contract.EndContractBlock();
+ return (sbyte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(uint value)
+ {
+ if (value > SByte.MaxValue) ThrowSByteOverflowException();
+ Contract.EndContractBlock();
+ return (sbyte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(long value)
+ {
+ if (value < SByte.MinValue || value > SByte.MaxValue) ThrowSByteOverflowException();
+ Contract.EndContractBlock();
+ return (sbyte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(ulong value)
+ {
+ if (value > (ulong)SByte.MaxValue) ThrowSByteOverflowException();
+ Contract.EndContractBlock();
+ return (sbyte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(float value)
+ {
+ return ToSByte((double)value);
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(double value)
+ {
+ return ToSByte(ToInt32(value));
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(decimal value)
+ {
+ return Decimal.ToSByte(Decimal.Round(value, 0));
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(String value)
+ {
+ if (value == null)
+ return 0;
+ return SByte.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(String value, IFormatProvider provider)
+ {
+ return SByte.Parse(value, NumberStyles.Integer, provider);
+ }
+
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(DateTime value)
+ {
+ return ((IConvertible)value).ToSByte(null);
+ }
+
+ // Disallowed conversions to SByte
+ // public static sbyte ToSByte(TimeSpan value)
+
+ // Conversions to Byte
+
+ public static byte ToByte(object value)
+ {
+ return value == null ? (byte)0 : ((IConvertible)value).ToByte(null);
+ }
+
+ public static byte ToByte(object value, IFormatProvider provider)
+ {
+ return value == null ? (byte)0 : ((IConvertible)value).ToByte(provider);
+ }
+
+ public static byte ToByte(bool value)
+ {
+ return value ? (byte)Boolean.True : (byte)Boolean.False;
+ }
+
+ public static byte ToByte(byte value)
+ {
+ return value;
+ }
+
+ public static byte ToByte(char value)
+ {
+ if (value > Byte.MaxValue) ThrowByteOverflowException();
+ Contract.EndContractBlock();
+ return (byte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static byte ToByte(sbyte value)
+ {
+ if (value < Byte.MinValue) ThrowByteOverflowException();
+ Contract.EndContractBlock();
+ return (byte)value;
+ }
+
+ public static byte ToByte(short value)
+ {
+ if (value < Byte.MinValue || value > Byte.MaxValue) ThrowByteOverflowException();
+ Contract.EndContractBlock();
+ return (byte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static byte ToByte(ushort value)
+ {
+ if (value > Byte.MaxValue) ThrowByteOverflowException();
+ Contract.EndContractBlock();
+ return (byte)value;
+ }
+
+ public static byte ToByte(int value)
+ {
+ if (value < Byte.MinValue || value > Byte.MaxValue) ThrowByteOverflowException();
+ Contract.EndContractBlock();
+ return (byte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static byte ToByte(uint value)
+ {
+ if (value > Byte.MaxValue) ThrowByteOverflowException();
+ Contract.EndContractBlock();
+ return (byte)value;
+ }
+
+ public static byte ToByte(long value)
+ {
+ if (value < Byte.MinValue || value > Byte.MaxValue) ThrowByteOverflowException();
+ Contract.EndContractBlock();
+ return (byte)value;
+ }
+
+ [CLSCompliant(false)]
+ public static byte ToByte(ulong value)
+ {
+ if (value > Byte.MaxValue) ThrowByteOverflowException();
+ Contract.EndContractBlock();
+ return (byte)value;
+ }
+
+ public static byte ToByte(float value)
+ {
+ return ToByte((double)value);
+ }
+
+ public static byte ToByte(double value)
+ {
+ return ToByte(ToInt32(value));
+ }
+
+ public static byte ToByte(decimal value)
+ {
+ return Decimal.ToByte(Decimal.Round(value, 0));
+ }
+
+ public static byte ToByte(String value)
+ {
+ if (value == null)
+ return 0;
+ return Byte.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ public static byte ToByte(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0;
+ return Byte.Parse(value, NumberStyles.Integer, provider);
+ }
+
+ public static byte ToByte(DateTime value)
+ {
+ return ((IConvertible)value).ToByte(null);
+ }
+
+
+ // Disallowed conversions to Byte
+ // public static byte ToByte(TimeSpan value)
+
+ // Conversions to Int16
+
+ public static short ToInt16(object value)
+ {
+ return value == null ? (short)0 : ((IConvertible)value).ToInt16(null);
+ }
+
+ public static short ToInt16(object value, IFormatProvider provider)
+ {
+ return value == null ? (short)0 : ((IConvertible)value).ToInt16(provider);
+ }
+
+ public static short ToInt16(bool value)
+ {
+ return value ? (short)Boolean.True : (short)Boolean.False;
+ }
+
+ public static short ToInt16(char value)
+ {
+ if (value > Int16.MaxValue) ThrowInt16OverflowException();
+ Contract.EndContractBlock();
+ return (short)value;
+ }
+
+ [CLSCompliant(false)]
+ public static short ToInt16(sbyte value)
+ {
+ return value;
+ }
+
+ public static short ToInt16(byte value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static short ToInt16(ushort value)
+ {
+ if (value > Int16.MaxValue) ThrowInt16OverflowException();
+ Contract.EndContractBlock();
+ return (short)value;
+ }
+
+ public static short ToInt16(int value)
+ {
+ if (value < Int16.MinValue || value > Int16.MaxValue) ThrowInt16OverflowException();
+ Contract.EndContractBlock();
+ return (short)value;
+ }
+
+ [CLSCompliant(false)]
+ public static short ToInt16(uint value)
+ {
+ if (value > Int16.MaxValue) ThrowInt16OverflowException();
+ Contract.EndContractBlock();
+ return (short)value;
+ }
+
+ public static short ToInt16(short value)
+ {
+ return value;
+ }
+
+ public static short ToInt16(long value)
+ {
+ if (value < Int16.MinValue || value > Int16.MaxValue) ThrowInt16OverflowException();
+ Contract.EndContractBlock();
+ return (short)value;
+ }
+
+ [CLSCompliant(false)]
+ public static short ToInt16(ulong value)
+ {
+ if (value > (ulong)Int16.MaxValue) ThrowInt16OverflowException();
+ Contract.EndContractBlock();
+ return (short)value;
+ }
+
+ public static short ToInt16(float value)
+ {
+ return ToInt16((double)value);
+ }
+
+ public static short ToInt16(double value)
+ {
+ return ToInt16(ToInt32(value));
+ }
+
+ public static short ToInt16(decimal value)
+ {
+ return Decimal.ToInt16(Decimal.Round(value, 0));
+ }
+
+ public static short ToInt16(String value)
+ {
+ if (value == null)
+ return 0;
+ return Int16.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ public static short ToInt16(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0;
+ return Int16.Parse(value, NumberStyles.Integer, provider);
+ }
+
+ public static short ToInt16(DateTime value)
+ {
+ return ((IConvertible)value).ToInt16(null);
+ }
+
+
+ // Disallowed conversions to Int16
+ // public static short ToInt16(TimeSpan value)
+
+ // Conversions to UInt16
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(object value)
+ {
+ return value == null ? (ushort)0 : ((IConvertible)value).ToUInt16(null);
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(object value, IFormatProvider provider)
+ {
+ return value == null ? (ushort)0 : ((IConvertible)value).ToUInt16(provider);
+ }
+
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(bool value)
+ {
+ return value ? (ushort)Boolean.True : (ushort)Boolean.False;
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(char value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(sbyte value)
+ {
+ if (value < 0) ThrowUInt16OverflowException();
+ Contract.EndContractBlock();
+ return (ushort)value;
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(byte value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(short value)
+ {
+ if (value < 0) ThrowUInt16OverflowException();
+ Contract.EndContractBlock();
+ return (ushort)value;
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(int value)
+ {
+ if (value < 0 || value > UInt16.MaxValue) ThrowUInt16OverflowException();
+ Contract.EndContractBlock();
+ return (ushort)value;
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(ushort value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(uint value)
+ {
+ if (value > UInt16.MaxValue) ThrowUInt16OverflowException();
+ Contract.EndContractBlock();
+ return (ushort)value;
+ }
+
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(long value)
+ {
+ if (value < 0 || value > UInt16.MaxValue) ThrowUInt16OverflowException();
+ Contract.EndContractBlock();
+ return (ushort)value;
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(ulong value)
+ {
+ if (value > UInt16.MaxValue) ThrowUInt16OverflowException();
+ Contract.EndContractBlock();
+ return (ushort)value;
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(float value)
+ {
+ return ToUInt16((double)value);
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(double value)
+ {
+ return ToUInt16(ToInt32(value));
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(decimal value)
+ {
+ return Decimal.ToUInt16(Decimal.Round(value, 0));
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(String value)
+ {
+ if (value == null)
+ return 0;
+ return UInt16.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0;
+ return UInt16.Parse(value, NumberStyles.Integer, provider);
+ }
+
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(DateTime value)
+ {
+ return ((IConvertible)value).ToUInt16(null);
+ }
+
+ // Disallowed conversions to UInt16
+ // public static ushort ToUInt16(TimeSpan value)
+
+ // Conversions to Int32
+
+ public static int ToInt32(object value)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToInt32(null);
+ }
+
+ public static int ToInt32(object value, IFormatProvider provider)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToInt32(provider);
+ }
+
+
+ public static int ToInt32(bool value)
+ {
+ return value ? Boolean.True : Boolean.False;
+ }
+
+ public static int ToInt32(char value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static int ToInt32(sbyte value)
+ {
+ return value;
+ }
+
+ public static int ToInt32(byte value)
+ {
+ return value;
+ }
+
+ public static int ToInt32(short value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static int ToInt32(ushort value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static int ToInt32(uint value)
+ {
+ if (value > Int32.MaxValue) ThrowInt32OverflowException();
+ Contract.EndContractBlock();
+ return (int)value;
+ }
+
+ public static int ToInt32(int value)
+ {
+ return value;
+ }
+
+ public static int ToInt32(long value)
+ {
+ if (value < Int32.MinValue || value > Int32.MaxValue) ThrowInt32OverflowException();
+ Contract.EndContractBlock();
+ return (int)value;
+ }
+
+ [CLSCompliant(false)]
+ public static int ToInt32(ulong value)
+ {
+ if (value > Int32.MaxValue) ThrowInt32OverflowException();
+ Contract.EndContractBlock();
+ return (int)value;
+ }
+
+ public static int ToInt32(float value)
+ {
+ return ToInt32((double)value);
+ }
+
+ public static int ToInt32(double value)
+ {
+ if (value >= 0)
+ {
+ if (value < 2147483647.5)
+ {
+ int result = (int)value;
+ double dif = value - result;
+ if (dif > 0.5 || dif == 0.5 && (result & 1) != 0) result++;
+ return result;
+ }
+ }
+ else
+ {
+ if (value >= -2147483648.5)
+ {
+ int result = (int)value;
+ double dif = value - result;
+ if (dif < -0.5 || dif == -0.5 && (result & 1) != 0) result--;
+ return result;
+ }
+ }
+ throw new OverflowException(SR.Overflow_Int32);
+ }
+
+ public static int ToInt32(decimal value)
+ {
+ return Decimal.ToInt32(Decimal.Round(value, 0));
+ }
+
+ public static int ToInt32(String value)
+ {
+ if (value == null)
+ return 0;
+ return Int32.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ public static int ToInt32(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0;
+ return Int32.Parse(value, NumberStyles.Integer, provider);
+ }
+
+ public static int ToInt32(DateTime value)
+ {
+ return ((IConvertible)value).ToInt32(null);
+ }
+
+
+ // Disallowed conversions to Int32
+ // public static int ToInt32(TimeSpan value)
+
+ // Conversions to UInt32
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(object value)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToUInt32(null);
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(object value, IFormatProvider provider)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToUInt32(provider);
+ }
+
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(bool value)
+ {
+ return value ? (uint)Boolean.True : (uint)Boolean.False;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(char value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(sbyte value)
+ {
+ if (value < 0) ThrowUInt32OverflowException();
+ Contract.EndContractBlock();
+ return (uint)value;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(byte value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(short value)
+ {
+ if (value < 0) ThrowUInt32OverflowException();
+ Contract.EndContractBlock();
+ return (uint)value;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(ushort value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(int value)
+ {
+ if (value < 0) ThrowUInt32OverflowException();
+ Contract.EndContractBlock();
+ return (uint)value;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(uint value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(long value)
+ {
+ if (value < 0 || value > UInt32.MaxValue) ThrowUInt32OverflowException();
+ Contract.EndContractBlock();
+ return (uint)value;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(ulong value)
+ {
+ if (value > UInt32.MaxValue) ThrowUInt32OverflowException();
+ Contract.EndContractBlock();
+ return (uint)value;
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(float value)
+ {
+ return ToUInt32((double)value);
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(double value)
+ {
+ if (value >= -0.5 && value < 4294967295.5)
+ {
+ uint result = (uint)value;
+ double dif = value - result;
+ if (dif > 0.5 || dif == 0.5 && (result & 1) != 0) result++;
+ return result;
+ }
+ throw new OverflowException(SR.Overflow_UInt32);
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(decimal value)
+ {
+ return Decimal.ToUInt32(Decimal.Round(value, 0));
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(String value)
+ {
+ if (value == null)
+ return 0;
+ return UInt32.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0;
+ return UInt32.Parse(value, NumberStyles.Integer, provider);
+ }
+
+ [CLSCompliant(false)]
+ public static uint ToUInt32(DateTime value)
+ {
+ return ((IConvertible)value).ToUInt32(null);
+ }
+
+ // Disallowed conversions to UInt32
+ // public static uint ToUInt32(TimeSpan value)
+
+ // Conversions to Int64
+
+ public static long ToInt64(object value)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToInt64(null);
+ }
+
+ public static long ToInt64(object value, IFormatProvider provider)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToInt64(provider);
+ }
+
+
+ public static long ToInt64(bool value)
+ {
+ return value ? Boolean.True : Boolean.False;
+ }
+
+ public static long ToInt64(char value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static long ToInt64(sbyte value)
+ {
+ return value;
+ }
+
+ public static long ToInt64(byte value)
+ {
+ return value;
+ }
+
+ public static long ToInt64(short value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static long ToInt64(ushort value)
+ {
+ return value;
+ }
+
+ public static long ToInt64(int value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static long ToInt64(uint value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static long ToInt64(ulong value)
+ {
+ if (value > Int64.MaxValue) ThrowInt64OverflowException();
+ Contract.EndContractBlock();
+ return (long)value;
+ }
+
+ public static long ToInt64(long value)
+ {
+ return value;
+ }
+
+
+ public static long ToInt64(float value)
+ {
+ return ToInt64((double)value);
+ }
+
+ public static long ToInt64(double value)
+ {
+ return checked((long)Math.Round(value));
+ }
+
+ public static long ToInt64(decimal value)
+ {
+ return Decimal.ToInt64(Decimal.Round(value, 0));
+ }
+
+ public static long ToInt64(string value)
+ {
+ if (value == null)
+ return 0;
+ return Int64.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ public static long ToInt64(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0;
+ return Int64.Parse(value, NumberStyles.Integer, provider);
+ }
+
+ public static long ToInt64(DateTime value)
+ {
+ return ((IConvertible)value).ToInt64(null);
+ }
+
+ // Disallowed conversions to Int64
+ // public static long ToInt64(TimeSpan value)
+
+ // Conversions to UInt64
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(object value)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToUInt64(null);
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(object value, IFormatProvider provider)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToUInt64(provider);
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(bool value)
+ {
+ return value ? (ulong)Boolean.True : (ulong)Boolean.False;
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(char value)
+ {
+ return value;
+ }
+
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(sbyte value)
+ {
+ if (value < 0) ThrowUInt64OverflowException();
+ Contract.EndContractBlock();
+ return (ulong)value;
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(byte value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(short value)
+ {
+ if (value < 0) ThrowUInt64OverflowException();
+ Contract.EndContractBlock();
+ return (ulong)value;
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(ushort value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(int value)
+ {
+ if (value < 0) ThrowUInt64OverflowException();
+ Contract.EndContractBlock();
+ return (ulong)value;
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(uint value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(long value)
+ {
+ if (value < 0) ThrowUInt64OverflowException();
+ Contract.EndContractBlock();
+ return (ulong)value;
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(UInt64 value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(float value)
+ {
+ return ToUInt64((double)value);
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(double value)
+ {
+ return checked((ulong)Math.Round(value));
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(decimal value)
+ {
+ return Decimal.ToUInt64(Decimal.Round(value, 0));
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(String value)
+ {
+ if (value == null)
+ return 0;
+ return UInt64.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0;
+ return UInt64.Parse(value, NumberStyles.Integer, provider);
+ }
+
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(DateTime value)
+ {
+ return ((IConvertible)value).ToUInt64(null);
+ }
+
+ // Disallowed conversions to UInt64
+ // public static ulong ToUInt64(TimeSpan value)
+
+ // Conversions to Single
+
+ public static float ToSingle(object value)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToSingle(null);
+ }
+
+ public static float ToSingle(object value, IFormatProvider provider)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToSingle(provider);
+ }
+
+ [CLSCompliant(false)]
+ public static float ToSingle(sbyte value)
+ {
+ return value;
+ }
+
+ public static float ToSingle(byte value)
+ {
+ return value;
+ }
+
+ public static float ToSingle(char value)
+ {
+ return ((IConvertible)value).ToSingle(null);
+ }
+
+ public static float ToSingle(short value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static float ToSingle(ushort value)
+ {
+ return value;
+ }
+
+ public static float ToSingle(int value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static float ToSingle(uint value)
+ {
+ return value;
+ }
+
+ public static float ToSingle(long value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static float ToSingle(ulong value)
+ {
+ return value;
+ }
+
+ public static float ToSingle(float value)
+ {
+ return value;
+ }
+
+ public static float ToSingle(double value)
+ {
+ return (float)value;
+ }
+
+ public static float ToSingle(decimal value)
+ {
+ return (float)value;
+ }
+
+ public static float ToSingle(String value)
+ {
+ if (value == null)
+ return 0;
+ return Single.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ public static float ToSingle(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0;
+ return Single.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, provider);
+ }
+
+
+ public static float ToSingle(bool value)
+ {
+ return value ? Boolean.True : Boolean.False;
+ }
+
+ public static float ToSingle(DateTime value)
+ {
+ return ((IConvertible)value).ToSingle(null);
+ }
+
+ // Disallowed conversions to Single
+ // public static float ToSingle(TimeSpan value)
+
+ // Conversions to Double
+
+ public static double ToDouble(object value)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToDouble(null);
+ }
+
+ public static double ToDouble(object value, IFormatProvider provider)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToDouble(provider);
+ }
+
+
+ [CLSCompliant(false)]
+ public static double ToDouble(sbyte value)
+ {
+ return value;
+ }
+
+ public static double ToDouble(byte value)
+ {
+ return value;
+ }
+
+ public static double ToDouble(short value)
+ {
+ return value;
+ }
+
+ public static double ToDouble(char value)
+ {
+ return ((IConvertible)value).ToDouble(null);
+ }
+
+ [CLSCompliant(false)]
+ public static double ToDouble(ushort value)
+ {
+ return value;
+ }
+
+ public static double ToDouble(int value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static double ToDouble(uint value)
+ {
+ return value;
+ }
+
+ public static double ToDouble(long value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static double ToDouble(ulong value)
+ {
+ return value;
+ }
+
+ public static double ToDouble(float value)
+ {
+ return value;
+ }
+
+ public static double ToDouble(double value)
+ {
+ return value;
+ }
+
+ public static double ToDouble(decimal value)
+ {
+ return (double)value;
+ }
+
+ public static double ToDouble(String value)
+ {
+ if (value == null)
+ return 0;
+ return Double.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ public static double ToDouble(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0;
+ return Double.Parse(value, NumberStyles.Float | NumberStyles.AllowThousands, provider);
+ }
+
+ public static double ToDouble(bool value)
+ {
+ return value ? Boolean.True : Boolean.False;
+ }
+
+ public static double ToDouble(DateTime value)
+ {
+ return ((IConvertible)value).ToDouble(null);
+ }
+
+ // Disallowed conversions to Double
+ // public static double ToDouble(TimeSpan value)
+
+ // Conversions to Decimal
+
+ public static decimal ToDecimal(object value)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToDecimal(null);
+ }
+
+ public static decimal ToDecimal(object value, IFormatProvider provider)
+ {
+ return value == null ? 0 : ((IConvertible)value).ToDecimal(provider);
+ }
+
+ [CLSCompliant(false)]
+ public static decimal ToDecimal(sbyte value)
+ {
+ return value;
+ }
+
+ public static decimal ToDecimal(byte value)
+ {
+ return value;
+ }
+
+ public static decimal ToDecimal(char value)
+ {
+ return ((IConvertible)value).ToDecimal(null);
+ }
+
+ public static decimal ToDecimal(short value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static decimal ToDecimal(ushort value)
+ {
+ return value;
+ }
+
+ public static decimal ToDecimal(int value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static decimal ToDecimal(uint value)
+ {
+ return value;
+ }
+
+ public static decimal ToDecimal(long value)
+ {
+ return value;
+ }
+
+ [CLSCompliant(false)]
+ public static decimal ToDecimal(ulong value)
+ {
+ return value;
+ }
+
+ public static decimal ToDecimal(float value)
+ {
+ return (decimal)value;
+ }
+
+ public static decimal ToDecimal(double value)
+ {
+ return (decimal)value;
+ }
+
+ public static decimal ToDecimal(String value)
+ {
+ if (value == null)
+ return 0m;
+ return Decimal.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ public static Decimal ToDecimal(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return 0m;
+ return Decimal.Parse(value, NumberStyles.Number, provider);
+ }
+
+ public static decimal ToDecimal(decimal value)
+ {
+ return value;
+ }
+
+ public static decimal ToDecimal(bool value)
+ {
+ return value ? Boolean.True : Boolean.False;
+ }
+
+ public static decimal ToDecimal(DateTime value)
+ {
+ return ((IConvertible)value).ToDecimal(null);
+ }
+
+ // Disallowed conversions to Decimal
+ // public static decimal ToDecimal(TimeSpan value)
+
+ // Conversions to DateTime
+
+ public static DateTime ToDateTime(DateTime value)
+ {
+ return value;
+ }
+
+ public static DateTime ToDateTime(object value)
+ {
+ return value == null ? DateTime.MinValue : ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(object value, IFormatProvider provider)
+ {
+ return value == null ? DateTime.MinValue : ((IConvertible)value).ToDateTime(provider);
+ }
+
+ public static DateTime ToDateTime(String value)
+ {
+ if (value == null)
+ return new DateTime(0);
+ return DateTime.Parse(value, CultureInfo.CurrentCulture);
+ }
+
+ public static DateTime ToDateTime(String value, IFormatProvider provider)
+ {
+ if (value == null)
+ return new DateTime(0);
+ return DateTime.Parse(value, provider);
+ }
+
+ [CLSCompliant(false)]
+ public static DateTime ToDateTime(sbyte value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(byte value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(short value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ [CLSCompliant(false)]
+ public static DateTime ToDateTime(ushort value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(int value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ [CLSCompliant(false)]
+ public static DateTime ToDateTime(uint value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(long value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ [CLSCompliant(false)]
+ public static DateTime ToDateTime(ulong value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(bool value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(char value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(float value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(double value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ public static DateTime ToDateTime(decimal value)
+ {
+ return ((IConvertible)value).ToDateTime(null);
+ }
+
+ // Disallowed conversions to DateTime
+ // public static DateTime ToDateTime(TimeSpan value)
+
+ // Conversions to String
+
+ public static string ToString(Object value)
+ {
+ return ToString(value, null);
+ }
+
+ public static string ToString(Object value, IFormatProvider provider)
+ {
+ IConvertible ic = value as IConvertible;
+ if (ic != null)
+ return ic.ToString(provider);
+ IFormattable formattable = value as IFormattable;
+ if (formattable != null)
+ return formattable.ToString(null, provider);
+ return value == null ? String.Empty : value.ToString();
+ }
+
+ public static string ToString(bool value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString();
+ }
+
+ public static string ToString(bool value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString();
+ }
+
+ public static string ToString(char value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return Char.ToString(value);
+ }
+
+ public static string ToString(char value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString();
+ }
+
+ [CLSCompliant(false)]
+ public static string ToString(sbyte value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ [CLSCompliant(false)]
+ public static string ToString(sbyte value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ public static string ToString(byte value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ public static string ToString(byte value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ public static string ToString(short value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ public static string ToString(short value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ [CLSCompliant(false)]
+ public static string ToString(ushort value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ [CLSCompliant(false)]
+ public static string ToString(ushort value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ public static string ToString(int value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ public static string ToString(int value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ [CLSCompliant(false)]
+ public static string ToString(uint value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ [CLSCompliant(false)]
+ public static string ToString(uint value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ public static string ToString(long value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ public static string ToString(long value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ [CLSCompliant(false)]
+ public static string ToString(ulong value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ [CLSCompliant(false)]
+ public static string ToString(ulong value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ public static string ToString(float value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ public static string ToString(float value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ public static string ToString(double value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ public static string ToString(double value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ public static string ToString(decimal value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(CultureInfo.CurrentCulture);
+ }
+
+ public static string ToString(Decimal value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ public static string ToString(DateTime value)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString();
+ }
+
+ public static string ToString(DateTime value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() != null);
+ return value.ToString(provider);
+ }
+
+ public static String ToString(String value)
+ {
+ Contract.Ensures(Contract.Result<string>() == value); // We were always skipping the null check here.
+ return value;
+ }
+
+ public static String ToString(String value, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<string>() == value); // We were always skipping the null check here.
+ return value; // avoid the null check
+ }
+
+
+ //
+ // Conversions which understand Base XXX numbers.
+ //
+ // Parses value in base base. base can only
+ // be 2, 8, 10, or 16. If base is 16, the number may be preceded
+ // by 0x; any other leading or trailing characters cause an error.
+ //
+ public static byte ToByte(String value, int fromBase)
+ {
+ if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned);
+ if (r < Byte.MinValue || r > Byte.MaxValue)
+ ThrowByteOverflowException();
+ return (byte)r;
+ }
+
+ // Parses value in base fromBase. fromBase can only
+ // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded
+ // by 0x; any other leading or trailing characters cause an error.
+ //
+ [CLSCompliant(false)]
+ public static sbyte ToSByte(String value, int fromBase)
+ {
+ if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI1);
+ if (fromBase != 10 && r <= Byte.MaxValue)
+ return (sbyte)r;
+
+ if (r < SByte.MinValue || r > SByte.MaxValue)
+ ThrowSByteOverflowException();
+ return (sbyte)r;
+ }
+
+ // Parses value in base fromBase. fromBase can only
+ // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded
+ // by 0x; any other leading or trailing characters cause an error.
+ //
+ public static short ToInt16(String value, int fromBase)
+ {
+ if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsI2);
+ if (fromBase != 10 && r <= UInt16.MaxValue)
+ return (short)r;
+
+ if (r < Int16.MinValue || r > Int16.MaxValue)
+ ThrowInt16OverflowException();
+ return (short)r;
+ }
+
+ // Parses value in base fromBase. fromBase can only
+ // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded
+ // by 0x; any other leading or trailing characters cause an error.
+ //
+ [CLSCompliant(false)]
+ public static ushort ToUInt16(String value, int fromBase)
+ {
+ if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ int r = ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight | ParseNumbers.TreatAsUnsigned);
+ if (r < UInt16.MinValue || r > UInt16.MaxValue)
+ ThrowUInt16OverflowException();
+ return (ushort)r;
+ }
+
+ // Parses value in base fromBase. fromBase can only
+ // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded
+ // by 0x; any other leading or trailing characters cause an error.
+ //
+ public static int ToInt32(String value, int fromBase)
+ {
+ if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ return ParseNumbers.StringToInt(value, fromBase, ParseNumbers.IsTight);
+ }
+
+ // Parses value in base fromBase. fromBase can only
+ // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded
+ // by 0x; any other leading or trailing characters cause an error.
+ //
+ [CLSCompliant(false)]
+ public static uint ToUInt32(String value, int fromBase)
+ {
+ if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ return (uint)ParseNumbers.StringToInt(value, fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight);
+ }
+
+ // Parses value in base fromBase. fromBase can only
+ // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded
+ // by 0x; any other leading or trailing characters cause an error.
+ //
+ public static long ToInt64(String value, int fromBase)
+ {
+ if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ return ParseNumbers.StringToLong(value, fromBase, ParseNumbers.IsTight);
+ }
+
+ // Parses value in base fromBase. fromBase can only
+ // be 2, 8, 10, or 16. If fromBase is 16, the number may be preceded
+ // by 0x; any other leading or trailing characters cause an error.
+ //
+ [CLSCompliant(false)]
+ public static ulong ToUInt64(String value, int fromBase)
+ {
+ if (fromBase != 2 && fromBase != 8 && fromBase != 10 && fromBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ return (ulong)ParseNumbers.StringToLong(value, fromBase, ParseNumbers.TreatAsUnsigned | ParseNumbers.IsTight);
+ }
+
+ // Convert the byte value to a string in base fromBase
+ public static String ToString(byte value, int toBase)
+ {
+ if (toBase != 2 && toBase != 8 && toBase != 10 && toBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ return ParseNumbers.IntToString((int)value, toBase, -1, ' ', ParseNumbers.PrintAsI1);
+ }
+
+ // Convert the Int16 value to a string in base fromBase
+ public static String ToString(short value, int toBase)
+ {
+ if (toBase != 2 && toBase != 8 && toBase != 10 && toBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ return ParseNumbers.IntToString((int)value, toBase, -1, ' ', ParseNumbers.PrintAsI2);
+ }
+
+ // Convert the Int32 value to a string in base toBase
+ public static String ToString(int value, int toBase)
+ {
+ if (toBase != 2 && toBase != 8 && toBase != 10 && toBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ return ParseNumbers.IntToString(value, toBase, -1, ' ', 0);
+ }
+
+ // Convert the Int64 value to a string in base toBase
+ public static String ToString(long value, int toBase)
+ {
+ if (toBase != 2 && toBase != 8 && toBase != 10 && toBase != 16)
+ {
+ throw new ArgumentException(SR.Arg_InvalidBase);
+ }
+ Contract.EndContractBlock();
+ return ParseNumbers.LongToString(value, toBase, -1, ' ', 0);
+ }
+
+ public static String ToBase64String(byte[] inArray)
+ {
+ if (inArray == null)
+ {
+ throw new ArgumentNullException(nameof(inArray));
+ }
+ Contract.Ensures(Contract.Result<string>() != null);
+ Contract.EndContractBlock();
+ return ToBase64String(inArray, 0, inArray.Length, Base64FormattingOptions.None);
+ }
+
+ public static String ToBase64String(byte[] inArray, Base64FormattingOptions options)
+ {
+ if (inArray == null)
+ {
+ throw new ArgumentNullException(nameof(inArray));
+ }
+ Contract.Ensures(Contract.Result<string>() != null);
+ Contract.EndContractBlock();
+ return ToBase64String(inArray, 0, inArray.Length, options);
+ }
+
+ public static String ToBase64String(byte[] inArray, int offset, int length)
+ {
+ return ToBase64String(inArray, offset, length, Base64FormattingOptions.None);
+ }
+
+ public static unsafe String ToBase64String(byte[] inArray, int offset, int length, Base64FormattingOptions options)
+ {
+ //Do data verfication
+ if (inArray == null)
+ throw new ArgumentNullException(nameof(inArray));
+ if (length < 0)
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index);
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_GenericPositive);
+ if (options < Base64FormattingOptions.None || options > Base64FormattingOptions.InsertLineBreaks)
+ throw new ArgumentException(string.Format(SR.Arg_EnumIllegalVal, (int)options));
+ Contract.Ensures(Contract.Result<string>() != null);
+ Contract.EndContractBlock();
+
+ int inArrayLength;
+ int stringLength;
+
+ inArrayLength = inArray.Length;
+ if (offset > (inArrayLength - length))
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_OffsetLength);
+
+ if (inArrayLength == 0)
+ return String.Empty;
+
+ bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks);
+ //Create the new string. This is the maximally required length.
+ stringLength = ToBase64_CalculateAndValidateOutputLength(length, insertLineBreaks);
+
+ string returnString = string.FastAllocateString(stringLength);
+ fixed (char* outChars = returnString)
+ {
+ fixed (byte* inData = &inArray[0])
+ {
+ int j = ConvertToBase64Array(outChars, inData, offset, length, insertLineBreaks);
+ Debug.Assert(returnString.Length == j, "returnString.Length == j");
+ return returnString;
+ }
+ }
+ }
+
+ public static int ToBase64CharArray(byte[] inArray, int offsetIn, int length, char[] outArray, int offsetOut)
+ {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ Contract.Ensures(Contract.Result<int>() <= outArray.Length);
+ Contract.EndContractBlock();
+
+ return ToBase64CharArray(inArray, offsetIn, length, outArray, offsetOut, Base64FormattingOptions.None);
+ }
+
+ public static unsafe int ToBase64CharArray(byte[] inArray, int offsetIn, int length, char[] outArray, int offsetOut, Base64FormattingOptions options)
+ {
+ //Do data verfication
+ if (inArray == null)
+ throw new ArgumentNullException(nameof(inArray));
+ if (outArray == null)
+ throw new ArgumentNullException(nameof(outArray));
+ if (length < 0)
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index);
+ if (offsetIn < 0)
+ throw new ArgumentOutOfRangeException(nameof(offsetIn), SR.ArgumentOutOfRange_GenericPositive);
+ if (offsetOut < 0)
+ throw new ArgumentOutOfRangeException(nameof(offsetOut), SR.ArgumentOutOfRange_GenericPositive);
+
+ if (options < Base64FormattingOptions.None || options > Base64FormattingOptions.InsertLineBreaks)
+ {
+ throw new ArgumentException(string.Format(SR.Arg_EnumIllegalVal, (int)options));
+ }
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ Contract.Ensures(Contract.Result<int>() <= outArray.Length);
+ Contract.EndContractBlock();
+
+
+ int retVal;
+
+ int inArrayLength;
+ int outArrayLength;
+ int numElementsToCopy;
+
+ inArrayLength = inArray.Length;
+
+ if (offsetIn > (int)(inArrayLength - length))
+ throw new ArgumentOutOfRangeException(nameof(offsetIn), SR.ArgumentOutOfRange_OffsetLength);
+
+ if (inArrayLength == 0)
+ return 0;
+
+ bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks);
+ //This is the maximally required length that must be available in the char array
+ outArrayLength = outArray.Length;
+
+ // Length of the char buffer required
+ numElementsToCopy = ToBase64_CalculateAndValidateOutputLength(length, insertLineBreaks);
+
+ if (offsetOut > (int)(outArrayLength - numElementsToCopy))
+ throw new ArgumentOutOfRangeException(nameof(offsetOut), SR.ArgumentOutOfRange_OffsetOut);
+
+ fixed (char* outChars = &outArray[offsetOut])
+ {
+ fixed (byte* inData = &inArray[0])
+ {
+ retVal = ConvertToBase64Array(outChars, inData, offsetIn, length, insertLineBreaks);
+ }
+ }
+
+ return retVal;
+ }
+
+ private static unsafe int ConvertToBase64Array(char* outChars, byte* inData, int offset, int length, bool insertLineBreaks)
+ {
+ int lengthmod3 = length % 3;
+ int calcLength = offset + (length - lengthmod3);
+ int j = 0;
+ int charcount = 0;
+ //Convert three bytes at a time to base64 notation. This will consume 4 chars.
+ int i;
+
+ // get a pointer to the base64Table to avoid unnecessary range checking
+ fixed (char* base64 = &base64Table[0])
+ {
+ for (i = offset; i < calcLength; i += 3)
+ {
+ if (insertLineBreaks)
+ {
+ if (charcount == base64LineBreakPosition)
+ {
+ outChars[j++] = '\r';
+ outChars[j++] = '\n';
+ charcount = 0;
+ }
+ charcount += 4;
+ }
+ outChars[j] = base64[(inData[i] & 0xfc) >> 2];
+ outChars[j + 1] = base64[((inData[i] & 0x03) << 4) | ((inData[i + 1] & 0xf0) >> 4)];
+ outChars[j + 2] = base64[((inData[i + 1] & 0x0f) << 2) | ((inData[i + 2] & 0xc0) >> 6)];
+ outChars[j + 3] = base64[(inData[i + 2] & 0x3f)];
+ j += 4;
+ }
+
+ //Where we left off before
+ i = calcLength;
+
+ if (insertLineBreaks && (lengthmod3 != 0) && (charcount == base64LineBreakPosition))
+ {
+ outChars[j++] = '\r';
+ outChars[j++] = '\n';
+ }
+
+ switch (lengthmod3)
+ {
+ case 2: //One character padding needed
+ outChars[j] = base64[(inData[i] & 0xfc) >> 2];
+ outChars[j + 1] = base64[((inData[i] & 0x03) << 4) | ((inData[i + 1] & 0xf0) >> 4)];
+ outChars[j + 2] = base64[(inData[i + 1] & 0x0f) << 2];
+ outChars[j + 3] = base64[64]; //Pad
+ j += 4;
+ break;
+ case 1: // Two character padding needed
+ outChars[j] = base64[(inData[i] & 0xfc) >> 2];
+ outChars[j + 1] = base64[(inData[i] & 0x03) << 4];
+ outChars[j + 2] = base64[64]; //Pad
+ outChars[j + 3] = base64[64]; //Pad
+ j += 4;
+ break;
+ }
+ }
+
+ return j;
+ }
+
+ private static int ToBase64_CalculateAndValidateOutputLength(int inputLength, bool insertLineBreaks)
+ {
+ long outlen = ((long)inputLength) / 3 * 4; // the base length - we want integer division here.
+ outlen += ((inputLength % 3) != 0) ? 4 : 0; // at most 4 more chars for the remainder
+
+ if (outlen == 0)
+ return 0;
+
+ if (insertLineBreaks)
+ {
+ long newLines = outlen / base64LineBreakPosition;
+ if ((outlen % base64LineBreakPosition) == 0)
+ {
+ --newLines;
+ }
+ outlen += newLines * 2; // the number of line break chars we'll add, "\r\n"
+ }
+
+ // If we overflow an int then we cannot allocate enough
+ // memory to output the value so throw
+ if (outlen > int.MaxValue)
+ throw new OutOfMemoryException();
+
+ return (int)outlen;
+ }
+
+
+ /// <summary>
+ /// Converts the specified string, which encodes binary data as Base64 digits, to the equivalent byte array.
+ /// </summary>
+ /// <param name="s">The string to convert</param>
+ /// <returns>The array of bytes represented by the specifed Base64 string.</returns>
+ public static Byte[] FromBase64String(String s)
+ {
+ // "s" is an unfortunate parameter name, but we need to keep it for backward compat.
+
+ if (s == null)
+ throw new ArgumentNullException(nameof(s));
+
+ Contract.EndContractBlock();
+
+ unsafe
+ {
+ fixed (Char* sPtr = s)
+ {
+ return FromBase64CharPtr(sPtr, s.Length);
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// Converts the specified range of a Char array, which encodes binary data as Base64 digits, to the equivalent byte array.
+ /// </summary>
+ /// <param name="inArray">Chars representing Base64 encoding characters</param>
+ /// <param name="offset">A position within the input array.</param>
+ /// <param name="length">Number of element to convert.</param>
+ /// <returns>The array of bytes represented by the specified Base64 encoding characters.</returns>
+ public static Byte[] FromBase64CharArray(Char[] inArray, Int32 offset, Int32 length)
+ {
+ if (inArray == null)
+ throw new ArgumentNullException(nameof(inArray));
+
+ if (length < 0)
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index);
+
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_GenericPositive);
+
+ if (offset > inArray.Length - length)
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_OffsetLength);
+
+ Contract.EndContractBlock();
+
+ if (inArray.Length == 0)
+ {
+ return Array.Empty<byte>();
+ }
+
+ unsafe
+ {
+ fixed (Char* inArrayPtr = &inArray[0])
+ {
+ return FromBase64CharPtr(inArrayPtr + offset, length);
+ }
+ }
+ }
+
+
+
+ /// <summary>
+ /// Convert Base64 encoding characters to bytes:
+ /// - Compute result length exactly by actually walking the input;
+ /// - Allocate new result array based on computation;
+ /// - Decode input into the new array;
+ /// </summary>
+ /// <param name="inputPtr">Pointer to the first input char</param>
+ /// <param name="inputLength">Number of input chars</param>
+ /// <returns></returns>
+ private static unsafe Byte[] FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
+ {
+ // The validity of parameters much be checked by callers, thus we are Critical here.
+
+ Debug.Assert(0 <= inputLength);
+
+ // We need to get rid of any trailing white spaces.
+ // Otherwise we would be rejecting input such as "abc= ":
+ while (inputLength > 0)
+ {
+ Int32 lastChar = inputPtr[inputLength - 1];
+ if (lastChar != (Int32)' ' && lastChar != (Int32)'\n' && lastChar != (Int32)'\r' && lastChar != (Int32)'\t')
+ break;
+ inputLength--;
+ }
+
+ // Compute the output length:
+ Int32 resultLength = FromBase64_ComputeResultLength(inputPtr, inputLength);
+
+ Debug.Assert(0 <= resultLength);
+
+ // resultLength can be zero. We will still enter FromBase64_Decode and process the input.
+ // It may either simply write no bytes (e.g. input = " ") or throw (e.g. input = "ab").
+
+ // Create result byte blob:
+ Byte[] decodedBytes = new Byte[resultLength];
+
+ // Convert Base64 chars into bytes:
+ Int32 actualResultLength;
+ fixed (Byte* decodedBytesPtr = decodedBytes)
+ actualResultLength = FromBase64_Decode(inputPtr, inputLength, decodedBytesPtr, resultLength);
+
+ // Note that actualResultLength can differ from resultLength if the caller is modifying the array
+ // as it is being converted. Silently ignore the failure.
+ // Consider throwing exception in an non in-place release.
+
+ // We are done:
+ return decodedBytes;
+ }
+
+
+ /// <summary>
+ /// Decode characters representing a Base64 encoding into bytes:
+ /// Walk the input. Every time 4 chars are read, convert them to the 3 corresponding output bytes.
+ /// This method is a bit lengthy on purpose. We are trying to avoid jumps to helpers in the loop
+ /// to aid performance.
+ /// </summary>
+ /// <param name="inputPtr">Pointer to first input char</param>
+ /// <param name="inputLength">Number of input chars</param>
+ /// <param name="destPtr">Pointer to location for the first result byte</param>
+ /// <param name="destLength">Max length of the preallocated result buffer</param>
+ /// <returns>If the result buffer was not large enough to write all result bytes, return -1;
+ /// Otherwise return the number of result bytes actually produced.</returns>
+ private static unsafe Int32 FromBase64_Decode(Char* startInputPtr, Int32 inputLength, Byte* startDestPtr, Int32 destLength)
+ {
+ // You may find this method weird to look at. It's written for performance, not aesthetics.
+ // You will find unrolled loops label jumps and bit manipulations.
+
+ const UInt32 intA = (UInt32)'A';
+ const UInt32 inta = (UInt32)'a';
+ const UInt32 int0 = (UInt32)'0';
+ const UInt32 intEq = (UInt32)'=';
+ const UInt32 intPlus = (UInt32)'+';
+ const UInt32 intSlash = (UInt32)'/';
+ const UInt32 intSpace = (UInt32)' ';
+ const UInt32 intTab = (UInt32)'\t';
+ const UInt32 intNLn = (UInt32)'\n';
+ const UInt32 intCRt = (UInt32)'\r';
+ const UInt32 intAtoZ = (UInt32)('Z' - 'A'); // = ('z' - 'a')
+ const UInt32 int0to9 = (UInt32)('9' - '0');
+
+ Char* inputPtr = startInputPtr;
+ Byte* destPtr = startDestPtr;
+
+ // Pointers to the end of input and output:
+ Char* endInputPtr = inputPtr + inputLength;
+ Byte* endDestPtr = destPtr + destLength;
+
+ // Current char code/value:
+ UInt32 currCode;
+
+ // This 4-byte integer will contain the 4 codes of the current 4-char group.
+ // Eeach char codes for 6 bits = 24 bits.
+ // The remaining byte will be FF, we use it as a marker when 4 chars have been processed.
+ UInt32 currBlockCodes = 0x000000FFu;
+
+ unchecked
+ {
+ while (true)
+ {
+ // break when done:
+ if (inputPtr >= endInputPtr)
+ goto _AllInputConsumed;
+
+ // Get current char:
+ currCode = (UInt32)(*inputPtr);
+ inputPtr++;
+
+ // Determine current char code:
+
+ if (currCode - intA <= intAtoZ)
+ currCode -= intA;
+
+ else if (currCode - inta <= intAtoZ)
+ currCode -= (inta - 26u);
+
+ else if (currCode - int0 <= int0to9)
+ currCode -= (int0 - 52u);
+
+ else
+ {
+ // Use the slower switch for less common cases:
+ switch (currCode)
+ {
+ // Significant chars:
+ case intPlus:
+ currCode = 62u;
+ break;
+
+ case intSlash:
+ currCode = 63u;
+ break;
+
+ // Legal no-value chars (we ignore these):
+ case intCRt:
+ case intNLn:
+ case intSpace:
+ case intTab:
+ continue;
+
+ // The equality char is only legal at the end of the input.
+ // Jump after the loop to make it easier for the JIT register predictor to do a good job for the loop itself:
+ case intEq:
+ goto _EqualityCharEncountered;
+
+ // Other chars are illegal:
+ default:
+ throw new FormatException(SR.Format_BadBase64Char);
+ }
+ }
+
+ // Ok, we got the code. Save it:
+ currBlockCodes = (currBlockCodes << 6) | currCode;
+
+ // Last bit in currBlockCodes will be on after in shifted right 4 times:
+ if ((currBlockCodes & 0x80000000u) != 0u)
+ {
+ if ((Int32)(endDestPtr - destPtr) < 3)
+ return -1;
+
+ *(destPtr) = (Byte)(currBlockCodes >> 16);
+ *(destPtr + 1) = (Byte)(currBlockCodes >> 8);
+ *(destPtr + 2) = (Byte)(currBlockCodes);
+ destPtr += 3;
+
+ currBlockCodes = 0x000000FFu;
+ }
+ }
+ } // unchecked while
+
+ // 'd be nice to have an assert that we never get here, but CS0162: Unreachable code detected.
+ // Debug.Assert(false, "We only leave the above loop by jumping; should never get here.");
+
+ // We jump here out of the loop if we hit an '=':
+ _EqualityCharEncountered:
+
+ Debug.Assert(currCode == intEq);
+
+ // Recall that inputPtr is now one position past where '=' was read.
+ // '=' can only be at the last input pos:
+ if (inputPtr == endInputPtr)
+ {
+ // Code is zero for trailing '=':
+ currBlockCodes <<= 6;
+
+ // The '=' did not complete a 4-group. The input must be bad:
+ if ((currBlockCodes & 0x80000000u) == 0u)
+ throw new FormatException(SR.Format_BadBase64CharArrayLength);
+
+ if ((int)(endDestPtr - destPtr) < 2) // Autch! We underestimated the output length!
+ return -1;
+
+ // We are good, store bytes form this past group. We had a single "=", so we take two bytes:
+ *(destPtr++) = (Byte)(currBlockCodes >> 16);
+ *(destPtr++) = (Byte)(currBlockCodes >> 8);
+
+ currBlockCodes = 0x000000FFu;
+ }
+ else
+ { // '=' can also be at the pre-last position iff the last is also a '=' excluding the white spaces:
+ // We need to get rid of any intermediate white spaces.
+ // Otherwise we would be rejecting input such as "abc= =":
+ while (inputPtr < (endInputPtr - 1))
+ {
+ Int32 lastChar = *(inputPtr);
+ if (lastChar != (Int32)' ' && lastChar != (Int32)'\n' && lastChar != (Int32)'\r' && lastChar != (Int32)'\t')
+ break;
+ inputPtr++;
+ }
+
+ if (inputPtr == (endInputPtr - 1) && *(inputPtr) == '=')
+ {
+ // Code is zero for each of the two '=':
+ currBlockCodes <<= 12;
+
+ // The '=' did not complete a 4-group. The input must be bad:
+ if ((currBlockCodes & 0x80000000u) == 0u)
+ throw new FormatException(SR.Format_BadBase64CharArrayLength);
+
+ if ((Int32)(endDestPtr - destPtr) < 1) // Autch! We underestimated the output length!
+ return -1;
+
+ // We are good, store bytes form this past group. We had a "==", so we take only one byte:
+ *(destPtr++) = (Byte)(currBlockCodes >> 16);
+
+ currBlockCodes = 0x000000FFu;
+ }
+ else // '=' is not ok at places other than the end:
+ throw new FormatException(SR.Format_BadBase64Char);
+ }
+
+ // We get here either from above or by jumping out of the loop:
+ _AllInputConsumed:
+
+ // The last block of chars has less than 4 items
+ if (currBlockCodes != 0x000000FFu)
+ throw new FormatException(SR.Format_BadBase64CharArrayLength);
+
+ // Return how many bytes were actually recovered:
+ return (Int32)(destPtr - startDestPtr);
+ } // Int32 FromBase64_Decode(...)
+
+
+ /// <summary>
+ /// Compute the number of bytes encoded in the specified Base 64 char array:
+ /// Walk the entire input counting white spaces and padding chars, then compute result length
+ /// based on 3 bytes per 4 chars.
+ /// </summary>
+ private static unsafe Int32 FromBase64_ComputeResultLength(Char* inputPtr, Int32 inputLength)
+ {
+ const UInt32 intEq = (UInt32)'=';
+ const UInt32 intSpace = (UInt32)' ';
+
+ Debug.Assert(0 <= inputLength);
+
+ Char* inputEndPtr = inputPtr + inputLength;
+ Int32 usefulInputLength = inputLength;
+ Int32 padding = 0;
+
+ while (inputPtr < inputEndPtr)
+ {
+ UInt32 c = (UInt32)(*inputPtr);
+ inputPtr++;
+
+ // We want to be as fast as possible and filter out spaces with as few comparisons as possible.
+ // We end up accepting a number of illegal chars as legal white-space chars.
+ // This is ok: as soon as we hit them during actual decode we will recognise them as illegal and throw.
+ if (c <= intSpace)
+ usefulInputLength--;
+
+ else if (c == intEq)
+ {
+ usefulInputLength--;
+ padding++;
+ }
+ }
+
+ Debug.Assert(0 <= usefulInputLength);
+
+ // For legal input, we can assume that 0 <= padding < 3. But it may be more for illegal input.
+ // We will notice it at decode when we see a '=' at the wrong place.
+ Debug.Assert(0 <= padding);
+
+ // Perf: reuse the variable that stored the number of '=' to store the number of bytes encoded by the
+ // last group that contains the '=':
+ if (padding != 0)
+ {
+ if (padding == 1)
+ padding = 2;
+ else if (padding == 2)
+ padding = 1;
+ else
+ throw new FormatException(SR.Format_BadBase64Char);
+ }
+
+ // Done:
+ return (usefulInputLength / 4) * 3 + padding;
+ }
+ } // class Convert
+} // namespace
+
diff --git a/src/mscorlib/shared/System/CurrentSystemTimeZone.cs b/src/mscorlib/shared/System/CurrentSystemTimeZone.cs
new file mode 100644
index 0000000000..2d848397a9
--- /dev/null
+++ b/src/mscorlib/shared/System/CurrentSystemTimeZone.cs
@@ -0,0 +1,199 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose:
+** This class represents the current system timezone. It is
+** the only meaningful implementation of the TimeZone class
+** available in this version.
+**
+** The only TimeZone that we support in version 1 is the
+** CurrentTimeZone as determined by the system timezone.
+**
+**
+============================================================*/
+
+using System;
+using System.Diagnostics.Contracts;
+using System.Text;
+using System.Collections;
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+
+namespace System
+{
+ [Obsolete("System.CurrentSystemTimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo.Local instead.")]
+ [Serializable]
+ internal partial class CurrentSystemTimeZone : TimeZone
+ {
+ // Standard offset in ticks to the Universal time if
+ // no daylight saving is in used.
+ // E.g. the offset for PST (Pacific Standard time) should be -8 * 60 * 60 * 1000 * 10000.
+ // (1 millisecond = 10000 ticks)
+ private long m_ticksOffset;
+ private String m_standardName;
+ private String m_daylightName;
+
+ internal CurrentSystemTimeZone()
+ {
+ TimeZoneInfo local = TimeZoneInfo.Local;
+
+ m_ticksOffset = local.BaseUtcOffset.Ticks;
+ m_standardName = local.StandardName;
+ m_daylightName = local.DaylightName;
+ }
+
+ public override String StandardName
+ {
+ get
+ {
+ return m_standardName;
+ }
+ }
+
+ public override String DaylightName
+ {
+ get
+ {
+ return m_daylightName;
+ }
+ }
+
+ internal long GetUtcOffsetFromUniversalTime(DateTime time, ref Boolean isAmbiguousLocalDst)
+ {
+ // Get the daylight changes for the year of the specified time.
+ TimeSpan offset = new TimeSpan(m_ticksOffset);
+ DaylightTime daylightTime = GetDaylightChanges(time.Year);
+ isAmbiguousLocalDst = false;
+
+ if (daylightTime == null || daylightTime.Delta.Ticks == 0)
+ {
+ return offset.Ticks;
+ }
+
+ // The start and end times represent the range of universal times that are in DST for that year.
+ // Within that there is an ambiguous hour, usually right at the end, but at the beginning in
+ // the unusual case of a negative daylight savings delta.
+ DateTime startTime = daylightTime.Start - offset;
+ DateTime endTime = daylightTime.End - offset - daylightTime.Delta;
+ DateTime ambiguousStart;
+ DateTime ambiguousEnd;
+
+ if (daylightTime.Delta.Ticks > 0)
+ {
+ ambiguousStart = endTime - daylightTime.Delta;
+ ambiguousEnd = endTime;
+ }
+ else
+ {
+ ambiguousStart = startTime;
+ ambiguousEnd = startTime - daylightTime.Delta;
+ }
+
+ Boolean isDst = false;
+ if (startTime > endTime)
+ {
+ // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
+ // Note, the summer in the southern hemisphere begins late in the year.
+ isDst = (time < endTime || time >= startTime);
+ }
+ else
+ {
+ // In northern hemisphere, the daylight saving time starts in the middle of the year.
+ isDst = (time >= startTime && time < endTime);
+ }
+
+ if (isDst)
+ {
+ offset += daylightTime.Delta;
+
+ // See if the resulting local time becomes ambiguous. This must be captured here or the
+ // DateTime will not be able to round-trip back to UTC accurately.
+ if (time >= ambiguousStart && time < ambiguousEnd)
+ {
+ isAmbiguousLocalDst = true;
+ }
+ }
+ return offset.Ticks;
+ }
+
+ public override DateTime ToLocalTime(DateTime time)
+ {
+ if (time.Kind == DateTimeKind.Local)
+ {
+ return time;
+ }
+ Boolean isAmbiguousLocalDst = false;
+ Int64 offset = GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst);
+ long tick = time.Ticks + offset;
+ if (tick > DateTime.MaxTicks)
+ {
+ return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
+ }
+ if (tick < DateTime.MinTicks)
+ {
+ return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
+ }
+ return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
+ }
+
+ public override DaylightTime GetDaylightChanges(int year)
+ {
+ if (year < 1 || year > 9999)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year), SR.Format(SR.ArgumentOutOfRange_Range, 1, 9999));
+ }
+
+ return GetCachedDaylightChanges(year);
+ }
+
+ private static DaylightTime CreateDaylightChanges(int year)
+ {
+ DaylightTime currentDaylightChanges = null;
+
+ if (TimeZoneInfo.Local.SupportsDaylightSavingTime)
+ {
+ DateTime start;
+ DateTime end;
+ TimeSpan delta;
+
+ foreach (var rule in TimeZoneInfo.Local.GetAdjustmentRules())
+ {
+ if (rule.DateStart.Year <= year && rule.DateEnd.Year >= year && rule.DaylightDelta != TimeSpan.Zero)
+ {
+ start = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionStart);
+ end = TimeZoneInfo.TransitionTimeToDateTime(year, rule.DaylightTransitionEnd);
+ delta = rule.DaylightDelta;
+
+ currentDaylightChanges = new DaylightTime(start, end, delta);
+ break;
+ }
+ }
+ }
+
+ if (currentDaylightChanges == null)
+ {
+ currentDaylightChanges = new DaylightTime(DateTime.MinValue, DateTime.MinValue, TimeSpan.Zero);
+ }
+
+ return currentDaylightChanges;
+ }
+
+ public override TimeSpan GetUtcOffset(DateTime time)
+ {
+ if (time.Kind == DateTimeKind.Utc)
+ {
+ return TimeSpan.Zero;
+ }
+ else
+ {
+ return new TimeSpan(TimeZone.CalculateUtcOffset(time, GetDaylightChanges(time.Year)).Ticks + m_ticksOffset);
+ }
+ }
+ } // class CurrentSystemTimeZone
+}
diff --git a/src/mscorlib/shared/System/DBNull.cs b/src/mscorlib/shared/System/DBNull.cs
new file mode 100644
index 0000000000..486eb72f2a
--- /dev/null
+++ b/src/mscorlib/shared/System/DBNull.cs
@@ -0,0 +1,119 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public sealed class DBNull : ISerializable, IConvertible
+ {
+ private DBNull()
+ {
+ }
+
+ private DBNull(SerializationInfo info, StreamingContext context)
+ {
+ throw new NotSupportedException(SR.NotSupported_DBNullSerial);
+ }
+
+ public static readonly DBNull Value = new DBNull();
+
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ UnitySerializationHolder.GetUnitySerializationInfo(info, UnitySerializationHolder.NullUnity, null, null);
+ }
+
+ public override string ToString()
+ {
+ return string.Empty;
+ }
+
+ public string ToString(IFormatProvider provider)
+ {
+ return string.Empty;
+ }
+
+ public TypeCode GetTypeCode()
+ {
+ return TypeCode.DBNull;
+ }
+
+ bool IConvertible.ToBoolean(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ char IConvertible.ToChar(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ sbyte IConvertible.ToSByte(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ byte IConvertible.ToByte(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ short IConvertible.ToInt16(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ ushort IConvertible.ToUInt16(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ int IConvertible.ToInt32(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ uint IConvertible.ToUInt32(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ long IConvertible.ToInt64(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ ulong IConvertible.ToUInt64(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ float IConvertible.ToSingle(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ double IConvertible.ToDouble(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ decimal IConvertible.ToDecimal(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ DateTime IConvertible.ToDateTime(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.InvalidCast_FromDBNull);
+ }
+
+ object IConvertible.ToType(Type type, IFormatProvider provider)
+ {
+ return Convert.DefaultToType((IConvertible)this, type, provider);
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/DataMisalignedException.cs b/src/mscorlib/shared/System/DataMisalignedException.cs
new file mode 100644
index 0000000000..b1991a048e
--- /dev/null
+++ b/src/mscorlib/shared/System/DataMisalignedException.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+** Purpose: The exception class for a misaligned access exception
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public sealed class DataMisalignedException : SystemException
+ {
+ public DataMisalignedException()
+ : base(SR.Arg_DataMisalignedException)
+ {
+ HResult = __HResults.COR_E_DATAMISALIGNED;
+ }
+
+ public DataMisalignedException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_DATAMISALIGNED;
+ }
+
+ public DataMisalignedException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_DATAMISALIGNED;
+ }
+
+ internal DataMisalignedException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/DateTime.cs b/src/mscorlib/shared/System/DateTime.cs
new file mode 100644
index 0000000000..ddb72da77d
--- /dev/null
+++ b/src/mscorlib/shared/System/DateTime.cs
@@ -0,0 +1,1516 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Threading;
+using System.Globalization;
+using System.Runtime;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using System.Runtime.Versioning;
+using System.Security;
+using CultureInfo = System.Globalization.CultureInfo;
+using Calendar = System.Globalization.Calendar;
+
+namespace System
+{
+
+ // This value type represents a date and time. Every DateTime
+ // object has a private field (Ticks) of type Int64 that stores the
+ // date and time as the number of 100 nanosecond intervals since
+ // 12:00 AM January 1, year 1 A.D. in the proleptic Gregorian Calendar.
+ //
+ // Starting from V2.0, DateTime also stored some context about its time
+ // zone in the form of a 3-state value representing Unspecified, Utc or
+ // Local. This is stored in the two top bits of the 64-bit numeric value
+ // with the remainder of the bits storing the tick count. This information
+ // is only used during time zone conversions and is not part of the
+ // identity of the DateTime. Thus, operations like Compare and Equals
+ // ignore this state. This is to stay compatible with earlier behavior
+ // and performance characteristics and to avoid forcing people into dealing
+ // with the effects of daylight savings. Note, that this has little effect
+ // on how the DateTime works except in a context where its specific time
+ // zone is needed, such as during conversions and some parsing and formatting
+ // cases.
+ //
+ // There is also 4th state stored that is a special type of Local value that
+ // is used to avoid data loss when round-tripping between local and UTC time.
+ // See below for more information on this 4th state, although it is
+ // effectively hidden from most users, who just see the 3-state DateTimeKind
+ // enumeration.
+ //
+ // For compatibility, DateTime does not serialize the Kind data when used in
+ // binary serialization.
+ //
+ // For a description of various calendar issues, look at
+ //
+ // Calendar Studies web site, at
+ // http://serendipity.nofadz.com/hermetic/cal_stud.htm.
+ //
+ //
+ [StructLayout(LayoutKind.Auto)]
+ [Serializable]
+ public partial struct DateTime : IComparable, IFormattable, IConvertible, IComparable<DateTime>, IEquatable<DateTime>, ISerializable
+ {
+ // Number of 100ns ticks per time unit
+ private const long TicksPerMillisecond = 10000;
+ private const long TicksPerSecond = TicksPerMillisecond * 1000;
+ private const long TicksPerMinute = TicksPerSecond * 60;
+ private const long TicksPerHour = TicksPerMinute * 60;
+ private const long TicksPerDay = TicksPerHour * 24;
+
+ // Number of milliseconds per time unit
+ private const int MillisPerSecond = 1000;
+ private const int MillisPerMinute = MillisPerSecond * 60;
+ private const int MillisPerHour = MillisPerMinute * 60;
+ private const int MillisPerDay = MillisPerHour * 24;
+
+ // Number of days in a non-leap year
+ private const int DaysPerYear = 365;
+ // Number of days in 4 years
+ private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461
+ // Number of days in 100 years
+ private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524
+ // Number of days in 400 years
+ private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097
+
+ // Number of days from 1/1/0001 to 12/31/1600
+ private const int DaysTo1601 = DaysPer400Years * 4; // 584388
+ // Number of days from 1/1/0001 to 12/30/1899
+ private const int DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367;
+ // Number of days from 1/1/0001 to 12/31/1969
+ internal const int DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear; // 719,162
+ // Number of days from 1/1/0001 to 12/31/9999
+ private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059
+
+ internal const long MinTicks = 0;
+ internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1;
+ private const long MaxMillis = (long)DaysTo10000 * MillisPerDay;
+
+ private const long TicksTo1970 = DaysTo1970 * TicksPerDay;
+ private const long FileTimeOffset = DaysTo1601 * TicksPerDay;
+ private const long DoubleDateOffset = DaysTo1899 * TicksPerDay;
+ // The minimum OA date is 0100/01/01 (Note it's year 100).
+ // The maximum OA date is 9999/12/31
+ private const long OADateMinAsTicks = (DaysPer100Years - DaysPerYear) * TicksPerDay;
+ // All OA dates must be greater than (not >=) OADateMinAsDouble
+ private const double OADateMinAsDouble = -657435.0;
+ // All OA dates must be less than (not <=) OADateMaxAsDouble
+ private const double OADateMaxAsDouble = 2958466.0;
+
+ private const int DatePartYear = 0;
+ private const int DatePartDayOfYear = 1;
+ private const int DatePartMonth = 2;
+ private const int DatePartDay = 3;
+
+ private static readonly int[] s_daysToMonth365 = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
+ private static readonly int[] s_daysToMonth366 = {
+ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
+
+ public static readonly DateTime MinValue = new DateTime(MinTicks, DateTimeKind.Unspecified);
+ public static readonly DateTime MaxValue = new DateTime(MaxTicks, DateTimeKind.Unspecified);
+
+ private const UInt64 TicksMask = 0x3FFFFFFFFFFFFFFF;
+ private const UInt64 FlagsMask = 0xC000000000000000;
+ private const UInt64 LocalMask = 0x8000000000000000;
+ private const Int64 TicksCeiling = 0x4000000000000000;
+ private const UInt64 KindUnspecified = 0x0000000000000000;
+ private const UInt64 KindUtc = 0x4000000000000000;
+ private const UInt64 KindLocal = 0x8000000000000000;
+ private const UInt64 KindLocalAmbiguousDst = 0xC000000000000000;
+ private const Int32 KindShift = 62;
+
+ private const String TicksField = "ticks";
+ private const String DateDataField = "_dateData";
+
+ // The data is stored as an unsigned 64-bit integeter
+ // Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value
+ // 12/31/9999 23:59:59.9999999
+ // Bits 63-64: A four-state value that describes the DateTimeKind value of the date time, with a 2nd
+ // value for the rare case where the date time is local, but is in an overlapped daylight
+ // savings time hour and it is in daylight savings time. This allows distinction of these
+ // otherwise ambiguous local times and prevents data loss when round tripping from Local to
+ // UTC time.
+ private UInt64 _dateData;
+
+ // Constructs a DateTime from a tick count. The ticks
+ // argument specifies the date as the number of 100-nanosecond intervals
+ // that have elapsed since 1/1/0001 12:00am.
+ //
+ public DateTime(long ticks)
+ {
+ if (ticks < MinTicks || ticks > MaxTicks)
+ throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks);
+ Contract.EndContractBlock();
+ _dateData = (UInt64)ticks;
+ }
+
+ private DateTime(UInt64 dateData)
+ {
+ this._dateData = dateData;
+ }
+
+ public DateTime(long ticks, DateTimeKind kind)
+ {
+ if (ticks < MinTicks || ticks > MaxTicks)
+ {
+ throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks);
+ }
+ if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind));
+ }
+ Contract.EndContractBlock();
+ _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift));
+ }
+
+ internal DateTime(long ticks, DateTimeKind kind, Boolean isAmbiguousDst)
+ {
+ if (ticks < MinTicks || ticks > MaxTicks)
+ {
+ throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_DateTimeBadTicks);
+ }
+ Debug.Assert(kind == DateTimeKind.Local, "Internal Constructor is for local times only");
+ Contract.EndContractBlock();
+ _dateData = ((UInt64)ticks | (isAmbiguousDst ? KindLocalAmbiguousDst : KindLocal));
+ }
+
+ // Constructs a DateTime from a given year, month, and day. The
+ // time-of-day of the resulting DateTime is always midnight.
+ //
+ public DateTime(int year, int month, int day)
+ {
+ _dateData = (UInt64)DateToTicks(year, month, day);
+ }
+
+ // Constructs a DateTime from a given year, month, and day for
+ // the specified calendar. The
+ // time-of-day of the resulting DateTime is always midnight.
+ //
+ public DateTime(int year, int month, int day, Calendar calendar)
+ : this(year, month, day, 0, 0, 0, calendar)
+ {
+ }
+
+ // Constructs a DateTime from a given year, month, day, hour,
+ // minute, and second.
+ //
+ public DateTime(int year, int month, int day, int hour, int minute, int second)
+ {
+ _dateData = (UInt64)(DateToTicks(year, month, day) + TimeToTicks(hour, minute, second));
+ }
+
+ public DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
+ {
+ if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind));
+ }
+ Contract.EndContractBlock();
+ Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);
+ _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift));
+ }
+
+ // Constructs a DateTime from a given year, month, day, hour,
+ // minute, and second for the specified calendar.
+ //
+ public DateTime(int year, int month, int day, int hour, int minute, int second, Calendar calendar)
+ {
+ if (calendar == null)
+ throw new ArgumentNullException(nameof(calendar));
+ Contract.EndContractBlock();
+ _dateData = (UInt64)calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks;
+ }
+
+ // Constructs a DateTime from a given year, month, day, hour,
+ // minute, and second.
+ //
+ public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond)
+ {
+ if (millisecond < 0 || millisecond >= MillisPerSecond)
+ {
+ throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1));
+ }
+ Contract.EndContractBlock();
+ Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);
+ ticks += millisecond * TicksPerMillisecond;
+ if (ticks < MinTicks || ticks > MaxTicks)
+ throw new ArgumentException(SR.Arg_DateTimeRange);
+ _dateData = (UInt64)ticks;
+ }
+
+ public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
+ {
+ if (millisecond < 0 || millisecond >= MillisPerSecond)
+ {
+ throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1));
+ }
+ if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind));
+ }
+ Contract.EndContractBlock();
+ Int64 ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);
+ ticks += millisecond * TicksPerMillisecond;
+ if (ticks < MinTicks || ticks > MaxTicks)
+ throw new ArgumentException(SR.Arg_DateTimeRange);
+ _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift));
+ }
+
+ // Constructs a DateTime from a given year, month, day, hour,
+ // minute, and second for the specified calendar.
+ //
+ public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar)
+ {
+ if (calendar == null)
+ throw new ArgumentNullException(nameof(calendar));
+ if (millisecond < 0 || millisecond >= MillisPerSecond)
+ {
+ throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1));
+ }
+ Contract.EndContractBlock();
+ Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks;
+ ticks += millisecond * TicksPerMillisecond;
+ if (ticks < MinTicks || ticks > MaxTicks)
+ throw new ArgumentException(SR.Arg_DateTimeRange);
+ _dateData = (UInt64)ticks;
+ }
+
+ public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind)
+ {
+ if (calendar == null)
+ throw new ArgumentNullException(nameof(calendar));
+ if (millisecond < 0 || millisecond >= MillisPerSecond)
+ {
+ throw new ArgumentOutOfRangeException(nameof(millisecond), SR.Format(SR.ArgumentOutOfRange_Range, 0, MillisPerSecond - 1));
+ }
+ if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof(kind));
+ }
+ Contract.EndContractBlock();
+ Int64 ticks = calendar.ToDateTime(year, month, day, hour, minute, second, 0).Ticks;
+ ticks += millisecond * TicksPerMillisecond;
+ if (ticks < MinTicks || ticks > MaxTicks)
+ throw new ArgumentException(SR.Arg_DateTimeRange);
+ _dateData = ((UInt64)ticks | ((UInt64)kind << KindShift));
+ }
+
+ private DateTime(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ Boolean foundTicks = false;
+ Boolean foundDateData = false;
+ Int64 serializedTicks = 0;
+ UInt64 serializedDateData = 0;
+
+
+ // Get the data
+ SerializationInfoEnumerator enumerator = info.GetEnumerator();
+ while (enumerator.MoveNext())
+ {
+ switch (enumerator.Name)
+ {
+ case TicksField:
+ serializedTicks = Convert.ToInt64(enumerator.Value, CultureInfo.InvariantCulture);
+ foundTicks = true;
+ break;
+ case DateDataField:
+ serializedDateData = Convert.ToUInt64(enumerator.Value, CultureInfo.InvariantCulture);
+ foundDateData = true;
+ break;
+ default:
+ // Ignore other fields for forward compatibility.
+ break;
+ }
+ }
+ if (foundDateData)
+ {
+ _dateData = serializedDateData;
+ }
+ else if (foundTicks)
+ {
+ _dateData = (UInt64)serializedTicks;
+ }
+ else
+ {
+ throw new SerializationException(SR.Serialization_MissingDateTimeData);
+ }
+ Int64 ticks = InternalTicks;
+ if (ticks < MinTicks || ticks > MaxTicks)
+ {
+ throw new SerializationException(SR.Serialization_DateTimeTicksOutOfRange);
+ }
+ }
+
+
+
+ internal Int64 InternalTicks
+ {
+ get
+ {
+ return (Int64)(_dateData & TicksMask);
+ }
+ }
+
+ private UInt64 InternalKind
+ {
+ get
+ {
+ return (_dateData & FlagsMask);
+ }
+ }
+
+ // Returns the DateTime resulting from adding the given
+ // TimeSpan to this DateTime.
+ //
+ public DateTime Add(TimeSpan value)
+ {
+ return AddTicks(value._ticks);
+ }
+
+ // Returns the DateTime resulting from adding a fractional number of
+ // time units to this DateTime.
+ private DateTime Add(double value, int scale)
+ {
+ long millis = (long)(value * scale + (value >= 0 ? 0.5 : -0.5));
+ if (millis <= -MaxMillis || millis >= MaxMillis)
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_AddValue);
+ return AddTicks(millis * TicksPerMillisecond);
+ }
+
+ // Returns the DateTime resulting from adding a fractional number of
+ // days to this DateTime. The result is computed by rounding the
+ // fractional number of days given by value to the nearest
+ // millisecond, and adding that interval to this DateTime. The
+ // value argument is permitted to be negative.
+ //
+ public DateTime AddDays(double value)
+ {
+ return Add(value, MillisPerDay);
+ }
+
+ // Returns the DateTime resulting from adding a fractional number of
+ // hours to this DateTime. The result is computed by rounding the
+ // fractional number of hours given by value to the nearest
+ // millisecond, and adding that interval to this DateTime. The
+ // value argument is permitted to be negative.
+ //
+ public DateTime AddHours(double value)
+ {
+ return Add(value, MillisPerHour);
+ }
+
+ // Returns the DateTime resulting from the given number of
+ // milliseconds to this DateTime. The result is computed by rounding
+ // the number of milliseconds given by value to the nearest integer,
+ // and adding that interval to this DateTime. The value
+ // argument is permitted to be negative.
+ //
+ public DateTime AddMilliseconds(double value)
+ {
+ return Add(value, 1);
+ }
+
+ // Returns the DateTime resulting from adding a fractional number of
+ // minutes to this DateTime. The result is computed by rounding the
+ // fractional number of minutes given by value to the nearest
+ // millisecond, and adding that interval to this DateTime. The
+ // value argument is permitted to be negative.
+ //
+ public DateTime AddMinutes(double value)
+ {
+ return Add(value, MillisPerMinute);
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // months to this DateTime. The result is computed by incrementing
+ // (or decrementing) the year and month parts of this DateTime by
+ // months months, and, if required, adjusting the day part of the
+ // resulting date downwards to the last day of the resulting month in the
+ // resulting year. The time-of-day part of the result is the same as the
+ // time-of-day part of this DateTime.
+ //
+ // In more precise terms, considering this DateTime to be of the
+ // form y / m / d + t, where y is the
+ // year, m is the month, d is the day, and t is the
+ // time-of-day, the result is y1 / m1 / d1 + t,
+ // where y1 and m1 are computed by adding months months
+ // to y and m, and d1 is the largest value less than
+ // or equal to d that denotes a valid day in month m1 of year
+ // y1.
+ //
+ public DateTime AddMonths(int months)
+ {
+ if (months < -120000 || months > 120000) throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateTimeBadMonths);
+ Contract.EndContractBlock();
+ int y = GetDatePart(DatePartYear);
+ int m = GetDatePart(DatePartMonth);
+ int d = GetDatePart(DatePartDay);
+ int i = m - 1 + months;
+ if (i >= 0)
+ {
+ m = i % 12 + 1;
+ y = y + i / 12;
+ }
+ else
+ {
+ m = 12 + (i + 1) % 12;
+ y = y + (i - 11) / 12;
+ }
+ if (y < 1 || y > 9999)
+ {
+ throw new ArgumentOutOfRangeException(nameof(months), SR.ArgumentOutOfRange_DateArithmetic);
+ }
+ int days = DaysInMonth(y, m);
+ if (d > days) d = days;
+ return new DateTime((UInt64)(DateToTicks(y, m, d) + InternalTicks % TicksPerDay) | InternalKind);
+ }
+
+ // Returns the DateTime resulting from adding a fractional number of
+ // seconds to this DateTime. The result is computed by rounding the
+ // fractional number of seconds given by value to the nearest
+ // millisecond, and adding that interval to this DateTime. The
+ // value argument is permitted to be negative.
+ //
+ public DateTime AddSeconds(double value)
+ {
+ return Add(value, MillisPerSecond);
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // 100-nanosecond ticks to this DateTime. The value argument
+ // is permitted to be negative.
+ //
+ public DateTime AddTicks(long value)
+ {
+ long ticks = InternalTicks;
+ if (value > MaxTicks - ticks || value < MinTicks - ticks)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_DateArithmetic);
+ }
+ return new DateTime((UInt64)(ticks + value) | InternalKind);
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // years to this DateTime. The result is computed by incrementing
+ // (or decrementing) the year part of this DateTime by value
+ // years. If the month and day of this DateTime is 2/29, and if the
+ // resulting year is not a leap year, the month and day of the resulting
+ // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
+ // parts of the result are the same as those of this DateTime.
+ //
+ public DateTime AddYears(int value)
+ {
+ if (value < -10000 || value > 10000)
+ {
+ // DateTimeOffset.AddYears(int years) is implemented on top of DateTime.AddYears(int value). Use the more appropriate
+ // parameter name out of the two for the exception.
+ throw new ArgumentOutOfRangeException("years", SR.ArgumentOutOfRange_DateTimeBadYears);
+ }
+ Contract.EndContractBlock();
+ return AddMonths(value * 12);
+ }
+
+ // Compares two DateTime values, returning an integer that indicates
+ // their relationship.
+ //
+ public static int Compare(DateTime t1, DateTime t2)
+ {
+ Int64 ticks1 = t1.InternalTicks;
+ Int64 ticks2 = t2.InternalTicks;
+ if (ticks1 > ticks2) return 1;
+ if (ticks1 < ticks2) return -1;
+ return 0;
+ }
+
+ // Compares this DateTime to a given object. This method provides an
+ // implementation of the IComparable interface. The object
+ // argument must be another DateTime, or otherwise an exception
+ // occurs. Null is considered less than any instance.
+ //
+ // Returns a value less than zero if this object
+ public int CompareTo(Object value)
+ {
+ if (value == null) return 1;
+ if (!(value is DateTime))
+ {
+ throw new ArgumentException(SR.Arg_MustBeDateTime);
+ }
+
+ return Compare(this, (DateTime)value);
+ }
+
+ public int CompareTo(DateTime value)
+ {
+ return Compare(this, value);
+ }
+
+ // Returns the tick count corresponding to the given year, month, and day.
+ // Will check the if the parameters are valid.
+ private static long DateToTicks(int year, int month, int day)
+ {
+ if (year >= 1 && year <= 9999 && month >= 1 && month <= 12)
+ {
+ int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365;
+ if (day >= 1 && day <= days[month] - days[month - 1])
+ {
+ int y = year - 1;
+ int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
+ return n * TicksPerDay;
+ }
+ }
+ throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
+ }
+
+ // Return the tick count corresponding to the given hour, minute, second.
+ // Will check the if the parameters are valid.
+ private static long TimeToTicks(int hour, int minute, int second)
+ {
+ //TimeSpan.TimeToTicks is a family access function which does no error checking, so
+ //we need to put some error checking out here.
+ if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60)
+ {
+ return (TimeSpan.TimeToTicks(hour, minute, second));
+ }
+ throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond);
+ }
+
+ // Returns the number of days in the month given by the year and
+ // month arguments.
+ //
+ public static int DaysInMonth(int year, int month)
+ {
+ if (month < 1 || month > 12) throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
+ Contract.EndContractBlock();
+ // IsLeapYear checks the year argument
+ int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365;
+ return days[month] - days[month - 1];
+ }
+
+ // Converts an OLE Date to a tick count.
+ // This function is duplicated in COMDateTime.cpp
+ internal static long DoubleDateToTicks(double value)
+ {
+ // The check done this way will take care of NaN
+ if (!(value < OADateMaxAsDouble) || !(value > OADateMinAsDouble))
+ throw new ArgumentException(SR.Arg_OleAutDateInvalid);
+
+ // Conversion to long will not cause an overflow here, as at this point the "value" is in between OADateMinAsDouble and OADateMaxAsDouble
+ long millis = (long)(value * MillisPerDay + (value >= 0 ? 0.5 : -0.5));
+ // The interesting thing here is when you have a value like 12.5 it all positive 12 days and 12 hours from 01/01/1899
+ // However if you a value of -12.25 it is minus 12 days but still positive 6 hours, almost as though you meant -11.75 all negative
+ // This line below fixes up the millis in the negative case
+ if (millis < 0)
+ {
+ millis -= (millis % MillisPerDay) * 2;
+ }
+
+ millis += DoubleDateOffset / TicksPerMillisecond;
+
+ if (millis < 0 || millis >= MaxMillis) throw new ArgumentException(SR.Arg_OleAutDateScale);
+ return millis * TicksPerMillisecond;
+ }
+
+ // Checks if this DateTime is equal to a given object. Returns
+ // true if the given object is a boxed DateTime and its value
+ // is equal to the value of this DateTime. Returns false
+ // otherwise.
+ //
+ public override bool Equals(Object value)
+ {
+ if (value is DateTime)
+ {
+ return InternalTicks == ((DateTime)value).InternalTicks;
+ }
+ return false;
+ }
+
+ public bool Equals(DateTime value)
+ {
+ return InternalTicks == value.InternalTicks;
+ }
+
+ // Compares two DateTime values for equality. Returns true if
+ // the two DateTime values are equal, or false if they are
+ // not equal.
+ //
+ public static bool Equals(DateTime t1, DateTime t2)
+ {
+ return t1.InternalTicks == t2.InternalTicks;
+ }
+
+ public static DateTime FromBinary(Int64 dateData)
+ {
+ if ((dateData & (unchecked((Int64)LocalMask))) != 0)
+ {
+ // Local times need to be adjusted as you move from one time zone to another,
+ // just as they are when serializing in text. As such the format for local times
+ // changes to store the ticks of the UTC time, but with flags that look like a
+ // local date.
+ Int64 ticks = dateData & (unchecked((Int64)TicksMask));
+ // Negative ticks are stored in the top part of the range and should be converted back into a negative number
+ if (ticks > TicksCeiling - TicksPerDay)
+ {
+ ticks = ticks - TicksCeiling;
+ }
+ // Convert the ticks back to local. If the UTC ticks are out of range, we need to default to
+ // the UTC offset from MinValue and MaxValue to be consistent with Parse.
+ Boolean isAmbiguousLocalDst = false;
+ Int64 offsetTicks;
+ if (ticks < MinTicks)
+ {
+ offsetTicks = TimeZoneInfo.GetLocalUtcOffset(DateTime.MinValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks;
+ }
+ else if (ticks > MaxTicks)
+ {
+ offsetTicks = TimeZoneInfo.GetLocalUtcOffset(DateTime.MaxValue, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks;
+ }
+ else
+ {
+ // Because the ticks conversion between UTC and local is lossy, we need to capture whether the
+ // time is in a repeated hour so that it can be passed to the DateTime constructor.
+ DateTime utcDt = new DateTime(ticks, DateTimeKind.Utc);
+ Boolean isDaylightSavings = false;
+ offsetTicks = TimeZoneInfo.GetUtcOffsetFromUtc(utcDt, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks;
+ }
+ ticks += offsetTicks;
+ // Another behaviour of parsing is to cause small times to wrap around, so that they can be used
+ // to compare times of day
+ if (ticks < 0)
+ {
+ ticks += TicksPerDay;
+ }
+ if (ticks < MinTicks || ticks > MaxTicks)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeBadBinaryData, nameof(dateData));
+ }
+ return new DateTime(ticks, DateTimeKind.Local, isAmbiguousLocalDst);
+ }
+ else
+ {
+ return DateTime.FromBinaryRaw(dateData);
+ }
+ }
+
+ // A version of ToBinary that uses the real representation and does not adjust local times. This is needed for
+ // scenarios where the serialized data must maintain compatibility
+ internal static DateTime FromBinaryRaw(Int64 dateData)
+ {
+ Int64 ticks = dateData & (Int64)TicksMask;
+ if (ticks < MinTicks || ticks > MaxTicks)
+ throw new ArgumentException(SR.Argument_DateTimeBadBinaryData, nameof(dateData));
+ return new DateTime((UInt64)dateData);
+ }
+
+ // Creates a DateTime from a Windows filetime. A Windows filetime is
+ // a long representing the date and time as the number of
+ // 100-nanosecond intervals that have elapsed since 1/1/1601 12:00am.
+ //
+ public static DateTime FromFileTime(long fileTime)
+ {
+ return FromFileTimeUtc(fileTime).ToLocalTime();
+ }
+
+ public static DateTime FromFileTimeUtc(long fileTime)
+ {
+ if (fileTime < 0 || fileTime > MaxTicks - FileTimeOffset)
+ {
+ throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_FileTimeInvalid);
+ }
+ Contract.EndContractBlock();
+
+ // This is the ticks in Universal time for this fileTime.
+ long universalTicks = fileTime + FileTimeOffset;
+ return new DateTime(universalTicks, DateTimeKind.Utc);
+ }
+
+ // Creates a DateTime from an OLE Automation Date.
+ //
+ public static DateTime FromOADate(double d)
+ {
+ return new DateTime(DoubleDateToTicks(d), DateTimeKind.Unspecified);
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+ Contract.EndContractBlock();
+
+ // Serialize both the old and the new format
+ info.AddValue(TicksField, InternalTicks);
+ info.AddValue(DateDataField, _dateData);
+ }
+
+ public Boolean IsDaylightSavingTime()
+ {
+ if (Kind == DateTimeKind.Utc)
+ {
+ return false;
+ }
+ return TimeZoneInfo.Local.IsDaylightSavingTime(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+ }
+
+ public static DateTime SpecifyKind(DateTime value, DateTimeKind kind)
+ {
+ return new DateTime(value.InternalTicks, kind);
+ }
+
+ public Int64 ToBinary()
+ {
+ if (Kind == DateTimeKind.Local)
+ {
+ // Local times need to be adjusted as you move from one time zone to another,
+ // just as they are when serializing in text. As such the format for local times
+ // changes to store the ticks of the UTC time, but with flags that look like a
+ // local date.
+
+ // To match serialization in text we need to be able to handle cases where
+ // the UTC value would be out of range. Unused parts of the ticks range are
+ // used for this, so that values just past max value are stored just past the
+ // end of the maximum range, and values just below minimum value are stored
+ // at the end of the ticks area, just below 2^62.
+ TimeSpan offset = TimeZoneInfo.GetLocalUtcOffset(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+ Int64 ticks = Ticks;
+ Int64 storedTicks = ticks - offset.Ticks;
+ if (storedTicks < 0)
+ {
+ storedTicks = TicksCeiling + storedTicks;
+ }
+ return storedTicks | (unchecked((Int64)LocalMask));
+ }
+ else
+ {
+ return (Int64)_dateData;
+ }
+ }
+
+ // Returns the date part of this DateTime. The resulting value
+ // corresponds to this DateTime with the time-of-day part set to
+ // zero (midnight).
+ //
+ public DateTime Date
+ {
+ get
+ {
+ Int64 ticks = InternalTicks;
+ return new DateTime((UInt64)(ticks - ticks % TicksPerDay) | InternalKind);
+ }
+ }
+
+ // Returns a given date part of this DateTime. This method is used
+ // to compute the year, day-of-year, month, or day part.
+ private int GetDatePart(int part)
+ {
+ Int64 ticks = InternalTicks;
+ // n = number of days since 1/1/0001
+ int n = (int)(ticks / TicksPerDay);
+ // y400 = number of whole 400-year periods since 1/1/0001
+ int y400 = n / DaysPer400Years;
+ // n = day number within 400-year period
+ n -= y400 * DaysPer400Years;
+ // y100 = number of whole 100-year periods within 400-year period
+ int y100 = n / DaysPer100Years;
+ // Last 100-year period has an extra day, so decrement result if 4
+ if (y100 == 4) y100 = 3;
+ // n = day number within 100-year period
+ n -= y100 * DaysPer100Years;
+ // y4 = number of whole 4-year periods within 100-year period
+ int y4 = n / DaysPer4Years;
+ // n = day number within 4-year period
+ n -= y4 * DaysPer4Years;
+ // y1 = number of whole years within 4-year period
+ int y1 = n / DaysPerYear;
+ // Last year has an extra day, so decrement result if 4
+ if (y1 == 4) y1 = 3;
+ // If year was requested, compute and return it
+ if (part == DatePartYear)
+ {
+ return y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1;
+ }
+ // n = day number within year
+ n -= y1 * DaysPerYear;
+ // If day-of-year was requested, return it
+ if (part == DatePartDayOfYear) return n + 1;
+ // Leap year calculation looks different from IsLeapYear since y1, y4,
+ // and y100 are relative to year 1, not year 0
+ bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3);
+ int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365;
+ // All months have less than 32 days, so n >> 5 is a good conservative
+ // estimate for the month
+ int m = (n >> 5) + 1;
+ // m = 1-based month number
+ while (n >= days[m]) m++;
+ // If month was requested, return it
+ if (part == DatePartMonth) return m;
+ // Return 1-based day-of-month
+ return n - days[m - 1] + 1;
+ }
+
+ // Returns the day-of-month part of this DateTime. The returned
+ // value is an integer between 1 and 31.
+ //
+ public int Day
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 1);
+ Contract.Ensures(Contract.Result<int>() <= 31);
+ return GetDatePart(DatePartDay);
+ }
+ }
+
+ // Returns the day-of-week part of this DateTime. The returned value
+ // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
+ // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
+ // Thursday, 5 indicates Friday, and 6 indicates Saturday.
+ //
+ public DayOfWeek DayOfWeek
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<DayOfWeek>() >= DayOfWeek.Sunday);
+ Contract.Ensures(Contract.Result<DayOfWeek>() <= DayOfWeek.Saturday);
+ return (DayOfWeek)((InternalTicks / TicksPerDay + 1) % 7);
+ }
+ }
+
+ // Returns the day-of-year part of this DateTime. The returned value
+ // is an integer between 1 and 366.
+ //
+ public int DayOfYear
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 1);
+ Contract.Ensures(Contract.Result<int>() <= 366); // leap year
+ return GetDatePart(DatePartDayOfYear);
+ }
+ }
+
+ // Returns the hash code for this DateTime.
+ //
+ public override int GetHashCode()
+ {
+ Int64 ticks = InternalTicks;
+ return unchecked((int)ticks) ^ (int)(ticks >> 32);
+ }
+
+ // Returns the hour part of this DateTime. The returned value is an
+ // integer between 0 and 23.
+ //
+ public int Hour
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ Contract.Ensures(Contract.Result<int>() < 24);
+ return (int)((InternalTicks / TicksPerHour) % 24);
+ }
+ }
+
+ internal Boolean IsAmbiguousDaylightSavingTime()
+ {
+ return (InternalKind == KindLocalAmbiguousDst);
+ }
+
+ [Pure]
+ public DateTimeKind Kind
+ {
+ get
+ {
+ switch (InternalKind)
+ {
+ case KindUnspecified:
+ return DateTimeKind.Unspecified;
+ case KindUtc:
+ return DateTimeKind.Utc;
+ default:
+ return DateTimeKind.Local;
+ }
+ }
+ }
+
+ // Returns the millisecond part of this DateTime. The returned value
+ // is an integer between 0 and 999.
+ //
+ public int Millisecond
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ Contract.Ensures(Contract.Result<int>() < 1000);
+ return (int)((InternalTicks / TicksPerMillisecond) % 1000);
+ }
+ }
+
+ // Returns the minute part of this DateTime. The returned value is
+ // an integer between 0 and 59.
+ //
+ public int Minute
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ Contract.Ensures(Contract.Result<int>() < 60);
+ return (int)((InternalTicks / TicksPerMinute) % 60);
+ }
+ }
+
+ // Returns the month part of this DateTime. The returned value is an
+ // integer between 1 and 12.
+ //
+ public int Month
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 1);
+ return GetDatePart(DatePartMonth);
+ }
+ }
+
+ // Returns a DateTime representing the current date and time. The
+ // resolution of the returned value depends on the system timer. For
+ // Windows NT 3.5 and later the timer resolution is approximately 10ms,
+ // for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98
+ // it is approximately 55ms.
+ //
+ public static DateTime Now
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<DateTime>().Kind == DateTimeKind.Local);
+
+ DateTime utc = UtcNow;
+ Boolean isAmbiguousLocalDst = false;
+ Int64 offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out isAmbiguousLocalDst).Ticks;
+ long tick = utc.Ticks + offset;
+ if (tick > DateTime.MaxTicks)
+ {
+ return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
+ }
+ if (tick < DateTime.MinTicks)
+ {
+ return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
+ }
+ return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
+ }
+ }
+
+ // Returns the second part of this DateTime. The returned value is
+ // an integer between 0 and 59.
+ //
+ public int Second
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ Contract.Ensures(Contract.Result<int>() < 60);
+ return (int)((InternalTicks / TicksPerSecond) % 60);
+ }
+ }
+
+ // Returns the tick count for this DateTime. The returned value is
+ // the number of 100-nanosecond intervals that have elapsed since 1/1/0001
+ // 12:00am.
+ //
+ public long Ticks
+ {
+ get
+ {
+ return InternalTicks;
+ }
+ }
+
+ // Returns the time-of-day part of this DateTime. The returned value
+ // is a TimeSpan that indicates the time elapsed since midnight.
+ //
+ public TimeSpan TimeOfDay
+ {
+ get
+ {
+ return new TimeSpan(InternalTicks % TicksPerDay);
+ }
+ }
+
+ // Returns a DateTime representing the current date. The date part
+ // of the returned value is the current date, and the time-of-day part of
+ // the returned value is zero (midnight).
+ //
+ public static DateTime Today
+ {
+ get
+ {
+ return DateTime.Now.Date;
+ }
+ }
+
+ // Returns the year part of this DateTime. The returned value is an
+ // integer between 1 and 9999.
+ //
+ public int Year
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 1 && Contract.Result<int>() <= 9999);
+ return GetDatePart(DatePartYear);
+ }
+ }
+
+ // Checks whether a given year is a leap year. This method returns true if
+ // year is a leap year, or false if not.
+ //
+ public static bool IsLeapYear(int year)
+ {
+ if (year < 1 || year > 9999)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year), SR.ArgumentOutOfRange_Year);
+ }
+ Contract.EndContractBlock();
+ return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
+ }
+
+ // Constructs a DateTime from a string. The string must specify a
+ // date and optionally a time in a culture-specific or universal format.
+ // Leading and trailing whitespace characters are allowed.
+ //
+ public static DateTime Parse(String s)
+ {
+ return (DateTimeParse.Parse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None));
+ }
+
+ // Constructs a DateTime from a string. The string must specify a
+ // date and optionally a time in a culture-specific or universal format.
+ // Leading and trailing whitespace characters are allowed.
+ //
+ public static DateTime Parse(String s, IFormatProvider provider)
+ {
+ return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None));
+ }
+
+ public static DateTime Parse(String s, IFormatProvider provider, DateTimeStyles styles)
+ {
+ DateTimeFormatInfo.ValidateStyles(styles, nameof(styles));
+ return (DateTimeParse.Parse(s, DateTimeFormatInfo.GetInstance(provider), styles));
+ }
+
+ // Constructs a DateTime from a string. The string must specify a
+ // date and optionally a time in a culture-specific or universal format.
+ // Leading and trailing whitespace characters are allowed.
+ //
+ public static DateTime ParseExact(String s, String format, IFormatProvider provider)
+ {
+ return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), DateTimeStyles.None));
+ }
+
+ // Constructs a DateTime from a string. The string must specify a
+ // date and optionally a time in a culture-specific or universal format.
+ // Leading and trailing whitespace characters are allowed.
+ //
+ public static DateTime ParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style)
+ {
+ DateTimeFormatInfo.ValidateStyles(style, nameof(style));
+ return (DateTimeParse.ParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style));
+ }
+
+ public static DateTime ParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style)
+ {
+ DateTimeFormatInfo.ValidateStyles(style, nameof(style));
+ return DateTimeParse.ParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style);
+ }
+
+ public TimeSpan Subtract(DateTime value)
+ {
+ return new TimeSpan(InternalTicks - value.InternalTicks);
+ }
+
+ public DateTime Subtract(TimeSpan value)
+ {
+ long ticks = InternalTicks;
+ long valueTicks = value._ticks;
+ if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_DateArithmetic);
+ }
+ return new DateTime((UInt64)(ticks - valueTicks) | InternalKind);
+ }
+
+ // This function is duplicated in COMDateTime.cpp
+ private static double TicksToOADate(long value)
+ {
+ if (value == 0)
+ return 0.0; // Returns OleAut's zero'ed date value.
+ if (value < TicksPerDay) // This is a fix for VB. They want the default day to be 1/1/0001 rathar then 12/30/1899.
+ value += DoubleDateOffset; // We could have moved this fix down but we would like to keep the bounds check.
+ if (value < OADateMinAsTicks)
+ throw new OverflowException(SR.Arg_OleAutDateInvalid);
+ // Currently, our max date == OA's max date (12/31/9999), so we don't
+ // need an overflow check in that direction.
+ long millis = (value - DoubleDateOffset) / TicksPerMillisecond;
+ if (millis < 0)
+ {
+ long frac = millis % MillisPerDay;
+ if (frac != 0) millis -= (MillisPerDay + frac) * 2;
+ }
+ return (double)millis / MillisPerDay;
+ }
+
+ // Converts the DateTime instance into an OLE Automation compatible
+ // double date.
+ public double ToOADate()
+ {
+ return TicksToOADate(InternalTicks);
+ }
+
+ public long ToFileTime()
+ {
+ // Treats the input as local if it is not specified
+ return ToUniversalTime().ToFileTimeUtc();
+ }
+
+ public long ToFileTimeUtc()
+ {
+ // Treats the input as universal if it is not specified
+ long ticks = ((InternalKind & LocalMask) != 0) ? ToUniversalTime().InternalTicks : this.InternalTicks;
+ ticks -= FileTimeOffset;
+ if (ticks < 0)
+ {
+ throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid);
+ }
+ return ticks;
+ }
+
+ public DateTime ToLocalTime()
+ {
+ return ToLocalTime(false);
+ }
+
+ internal DateTime ToLocalTime(bool throwOnOverflow)
+ {
+ if (Kind == DateTimeKind.Local)
+ {
+ return this;
+ }
+ Boolean isDaylightSavings = false;
+ Boolean isAmbiguousLocalDst = false;
+ Int64 offset = TimeZoneInfo.GetUtcOffsetFromUtc(this, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks;
+ long tick = Ticks + offset;
+ if (tick > DateTime.MaxTicks)
+ {
+ if (throwOnOverflow)
+ throw new ArgumentException(SR.Arg_ArgumentOutOfRangeException);
+ else
+ return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
+ }
+ if (tick < DateTime.MinTicks)
+ {
+ if (throwOnOverflow)
+ throw new ArgumentException(SR.Arg_ArgumentOutOfRangeException);
+ else
+ return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
+ }
+ return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
+ }
+
+ public String ToLongDateString()
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return DateTimeFormat.Format(this, "D", DateTimeFormatInfo.CurrentInfo);
+ }
+
+ public String ToLongTimeString()
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return DateTimeFormat.Format(this, "T", DateTimeFormatInfo.CurrentInfo);
+ }
+
+ public String ToShortDateString()
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return DateTimeFormat.Format(this, "d", DateTimeFormatInfo.CurrentInfo);
+ }
+
+ public String ToShortTimeString()
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return DateTimeFormat.Format(this, "t", DateTimeFormatInfo.CurrentInfo);
+ }
+
+ public override String ToString()
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return DateTimeFormat.Format(this, null, DateTimeFormatInfo.CurrentInfo);
+ }
+
+ public String ToString(String format)
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return DateTimeFormat.Format(this, format, DateTimeFormatInfo.CurrentInfo);
+ }
+
+ public String ToString(IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return DateTimeFormat.Format(this, null, DateTimeFormatInfo.GetInstance(provider));
+ }
+
+ public String ToString(String format, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+ return DateTimeFormat.Format(this, format, DateTimeFormatInfo.GetInstance(provider));
+ }
+
+ public DateTime ToUniversalTime()
+ {
+ return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+ }
+
+ public static Boolean TryParse(String s, out DateTime result)
+ {
+ return DateTimeParse.TryParse(s, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out result);
+ }
+
+ public static Boolean TryParse(String s, IFormatProvider provider, DateTimeStyles styles, out DateTime result)
+ {
+ DateTimeFormatInfo.ValidateStyles(styles, nameof(styles));
+ return DateTimeParse.TryParse(s, DateTimeFormatInfo.GetInstance(provider), styles, out result);
+ }
+
+ public static Boolean TryParseExact(String s, String format, IFormatProvider provider, DateTimeStyles style, out DateTime result)
+ {
+ DateTimeFormatInfo.ValidateStyles(style, nameof(style));
+ return DateTimeParse.TryParseExact(s, format, DateTimeFormatInfo.GetInstance(provider), style, out result);
+ }
+
+ public static Boolean TryParseExact(String s, String[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result)
+ {
+ DateTimeFormatInfo.ValidateStyles(style, nameof(style));
+ return DateTimeParse.TryParseExactMultiple(s, formats, DateTimeFormatInfo.GetInstance(provider), style, out result);
+ }
+
+ public static DateTime operator +(DateTime d, TimeSpan t)
+ {
+ long ticks = d.InternalTicks;
+ long valueTicks = t._ticks;
+ if (valueTicks > MaxTicks - ticks || valueTicks < MinTicks - ticks)
+ {
+ throw new ArgumentOutOfRangeException(nameof(t), SR.ArgumentOutOfRange_DateArithmetic);
+ }
+ return new DateTime((UInt64)(ticks + valueTicks) | d.InternalKind);
+ }
+
+ public static DateTime operator -(DateTime d, TimeSpan t)
+ {
+ long ticks = d.InternalTicks;
+ long valueTicks = t._ticks;
+ if (ticks - MinTicks < valueTicks || ticks - MaxTicks > valueTicks)
+ {
+ throw new ArgumentOutOfRangeException(nameof(t), SR.ArgumentOutOfRange_DateArithmetic);
+ }
+ return new DateTime((UInt64)(ticks - valueTicks) | d.InternalKind);
+ }
+
+ public static TimeSpan operator -(DateTime d1, DateTime d2)
+ {
+ return new TimeSpan(d1.InternalTicks - d2.InternalTicks);
+ }
+
+ public static bool operator ==(DateTime d1, DateTime d2)
+ {
+ return d1.InternalTicks == d2.InternalTicks;
+ }
+
+ public static bool operator !=(DateTime d1, DateTime d2)
+ {
+ return d1.InternalTicks != d2.InternalTicks;
+ }
+
+ public static bool operator <(DateTime t1, DateTime t2)
+ {
+ return t1.InternalTicks < t2.InternalTicks;
+ }
+
+ public static bool operator <=(DateTime t1, DateTime t2)
+ {
+ return t1.InternalTicks <= t2.InternalTicks;
+ }
+
+ public static bool operator >(DateTime t1, DateTime t2)
+ {
+ return t1.InternalTicks > t2.InternalTicks;
+ }
+
+ public static bool operator >=(DateTime t1, DateTime t2)
+ {
+ return t1.InternalTicks >= t2.InternalTicks;
+ }
+
+
+ // Returns a string array containing all of the known date and time options for the
+ // current culture. The strings returned are properly formatted date and
+ // time strings for the current instance of DateTime.
+ public String[] GetDateTimeFormats()
+ {
+ Contract.Ensures(Contract.Result<String[]>() != null);
+ return (GetDateTimeFormats(CultureInfo.CurrentCulture));
+ }
+
+ // Returns a string array containing all of the known date and time options for the
+ // using the information provided by IFormatProvider. The strings returned are properly formatted date and
+ // time strings for the current instance of DateTime.
+ public String[] GetDateTimeFormats(IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<String[]>() != null);
+ return (DateTimeFormat.GetAllDateTimes(this, DateTimeFormatInfo.GetInstance(provider)));
+ }
+
+
+ // Returns a string array containing all of the date and time options for the
+ // given format format and current culture. The strings returned are properly formatted date and
+ // time strings for the current instance of DateTime.
+ public String[] GetDateTimeFormats(char format)
+ {
+ Contract.Ensures(Contract.Result<String[]>() != null);
+ return (GetDateTimeFormats(format, CultureInfo.CurrentCulture));
+ }
+
+ // Returns a string array containing all of the date and time options for the
+ // given format format and given culture. The strings returned are properly formatted date and
+ // time strings for the current instance of DateTime.
+ public String[] GetDateTimeFormats(char format, IFormatProvider provider)
+ {
+ Contract.Ensures(Contract.Result<String[]>() != null);
+ return (DateTimeFormat.GetAllDateTimes(this, format, DateTimeFormatInfo.GetInstance(provider)));
+ }
+
+ //
+ // IConvertible implementation
+ //
+
+ public TypeCode GetTypeCode()
+ {
+ return TypeCode.DateTime;
+ }
+
+
+ bool IConvertible.ToBoolean(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Boolean"));
+ }
+
+ char IConvertible.ToChar(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Char"));
+ }
+
+ sbyte IConvertible.ToSByte(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "SByte"));
+ }
+
+ byte IConvertible.ToByte(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Byte"));
+ }
+
+ short IConvertible.ToInt16(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int16"));
+ }
+
+ ushort IConvertible.ToUInt16(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt16"));
+ }
+
+ int IConvertible.ToInt32(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int32"));
+ }
+
+ uint IConvertible.ToUInt32(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt32"));
+ }
+
+ long IConvertible.ToInt64(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Int64"));
+ }
+
+ ulong IConvertible.ToUInt64(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "UInt64"));
+ }
+
+ float IConvertible.ToSingle(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Single"));
+ }
+
+ double IConvertible.ToDouble(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Double"));
+ }
+
+ Decimal IConvertible.ToDecimal(IFormatProvider provider)
+ {
+ throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "DateTime", "Decimal"));
+ }
+
+ DateTime IConvertible.ToDateTime(IFormatProvider provider)
+ {
+ return this;
+ }
+
+ Object IConvertible.ToType(Type type, IFormatProvider provider)
+ {
+ return Convert.DefaultToType((IConvertible)this, type, provider);
+ }
+
+ // Tries to construct a DateTime from a given year, month, day, hour,
+ // minute, second and millisecond.
+ //
+ internal static Boolean TryCreate(int year, int month, int day, int hour, int minute, int second, int millisecond, out DateTime result)
+ {
+ result = DateTime.MinValue;
+ if (year < 1 || year > 9999 || month < 1 || month > 12)
+ {
+ return false;
+ }
+ int[] days = IsLeapYear(year) ? s_daysToMonth366 : s_daysToMonth365;
+ if (day < 1 || day > days[month] - days[month - 1])
+ {
+ return false;
+ }
+ if (hour < 0 || hour >= 24 || minute < 0 || minute >= 60 || second < 0 || second >= 60)
+ {
+ return false;
+ }
+ if (millisecond < 0 || millisecond >= MillisPerSecond)
+ {
+ return false;
+ }
+ long ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);
+
+ ticks += millisecond * TicksPerMillisecond;
+ if (ticks < MinTicks || ticks > MaxTicks)
+ {
+ return false;
+ }
+ result = new DateTime(ticks, DateTimeKind.Unspecified);
+ return true;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/DateTimeKind.cs b/src/mscorlib/shared/System/DateTimeKind.cs
new file mode 100644
index 0000000000..6b5e690df0
--- /dev/null
+++ b/src/mscorlib/shared/System/DateTimeKind.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ // This enum is used to indentify DateTime instances in cases when they are known to be in local time,
+ // UTC time or if this information has not been specified or is not applicable.
+
+ [Serializable]
+ public enum DateTimeKind
+ {
+ Unspecified = 0,
+ Utc = 1,
+ Local = 2,
+ }
+}
diff --git a/src/mscorlib/shared/System/DateTimeOffset.cs b/src/mscorlib/shared/System/DateTimeOffset.cs
new file mode 100644
index 0000000000..d5ccbd9195
--- /dev/null
+++ b/src/mscorlib/shared/System/DateTimeOffset.cs
@@ -0,0 +1,921 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace System
+{
+ // DateTimeOffset is a value type that consists of a DateTime and a time zone offset,
+ // ie. how far away the time is from GMT. The DateTime is stored whole, and the offset
+ // is stored as an Int16 internally to save space, but presented as a TimeSpan.
+ //
+ // The range is constrained so that both the represented clock time and the represented
+ // UTC time fit within the boundaries of MaxValue. This gives it the same range as DateTime
+ // for actual UTC times, and a slightly constrained range on one end when an offset is
+ // present.
+ //
+ // This class should be substitutable for date time in most cases; so most operations
+ // effectively work on the clock time. However, the underlying UTC time is what counts
+ // for the purposes of identity, sorting and subtracting two instances.
+ //
+ //
+ // There are theoretically two date times stored, the UTC and the relative local representation
+ // or the 'clock' time. It actually does not matter which is stored in m_dateTime, so it is desirable
+ // for most methods to go through the helpers UtcDateTime and ClockDateTime both to abstract this
+ // out and for internal readability.
+
+ [StructLayout(LayoutKind.Auto)]
+ [Serializable]
+ public struct DateTimeOffset : IComparable, IFormattable, IComparable<DateTimeOffset>, IEquatable<DateTimeOffset>, ISerializable, IDeserializationCallback
+ {
+ // Constants
+ internal const Int64 MaxOffset = TimeSpan.TicksPerHour * 14;
+ internal const Int64 MinOffset = -MaxOffset;
+
+ private const long UnixEpochTicks = TimeSpan.TicksPerDay * DateTime.DaysTo1970; // 621,355,968,000,000,000
+ private const long UnixEpochSeconds = UnixEpochTicks / TimeSpan.TicksPerSecond; // 62,135,596,800
+ private const long UnixEpochMilliseconds = UnixEpochTicks / TimeSpan.TicksPerMillisecond; // 62,135,596,800,000
+
+ internal const long UnixMinSeconds = DateTime.MinTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds;
+ internal const long UnixMaxSeconds = DateTime.MaxTicks / TimeSpan.TicksPerSecond - UnixEpochSeconds;
+
+ // Static Fields
+ public static readonly DateTimeOffset MinValue = new DateTimeOffset(DateTime.MinTicks, TimeSpan.Zero);
+ public static readonly DateTimeOffset MaxValue = new DateTimeOffset(DateTime.MaxTicks, TimeSpan.Zero);
+
+ // Instance Fields
+ private DateTime _dateTime;
+ private Int16 _offsetMinutes;
+
+ // Constructors
+
+ // Constructs a DateTimeOffset from a tick count and offset
+ public DateTimeOffset(long ticks, TimeSpan offset)
+ {
+ _offsetMinutes = ValidateOffset(offset);
+ // Let the DateTime constructor do the range checks
+ DateTime dateTime = new DateTime(ticks);
+ _dateTime = ValidateDate(dateTime, offset);
+ }
+
+ // Constructs a DateTimeOffset from a DateTime. For Local and Unspecified kinds,
+ // extracts the local offset. For UTC, creates a UTC instance with a zero offset.
+ public DateTimeOffset(DateTime dateTime)
+ {
+ TimeSpan offset;
+ if (dateTime.Kind != DateTimeKind.Utc)
+ {
+ // Local and Unspecified are both treated as Local
+ offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+ }
+ else
+ {
+ offset = new TimeSpan(0);
+ }
+ _offsetMinutes = ValidateOffset(offset);
+ _dateTime = ValidateDate(dateTime, offset);
+ }
+
+ // Constructs a DateTimeOffset from a DateTime. And an offset. Always makes the clock time
+ // consistent with the DateTime. For Utc ensures the offset is zero. For local, ensures that
+ // the offset corresponds to the local.
+ public DateTimeOffset(DateTime dateTime, TimeSpan offset)
+ {
+ if (dateTime.Kind == DateTimeKind.Local)
+ {
+ if (offset != TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime))
+ {
+ throw new ArgumentException(SR.Argument_OffsetLocalMismatch, nameof(offset));
+ }
+ }
+ else if (dateTime.Kind == DateTimeKind.Utc)
+ {
+ if (offset != TimeSpan.Zero)
+ {
+ throw new ArgumentException(SR.Argument_OffsetUtcMismatch, nameof(offset));
+ }
+ }
+ _offsetMinutes = ValidateOffset(offset);
+ _dateTime = ValidateDate(dateTime, offset);
+ }
+
+ // Constructs a DateTimeOffset from a given year, month, day, hour,
+ // minute, second and offset.
+ public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, TimeSpan offset)
+ {
+ _offsetMinutes = ValidateOffset(offset);
+ _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second), offset);
+ }
+
+ // Constructs a DateTimeOffset from a given year, month, day, hour,
+ // minute, second, millsecond and offset
+ public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset)
+ {
+ _offsetMinutes = ValidateOffset(offset);
+ _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second, millisecond), offset);
+ }
+
+ // Constructs a DateTimeOffset from a given year, month, day, hour,
+ // minute, second, millsecond, Calendar and offset.
+ public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset)
+ {
+ _offsetMinutes = ValidateOffset(offset);
+ _dateTime = ValidateDate(new DateTime(year, month, day, hour, minute, second, millisecond, calendar), offset);
+ }
+
+ // Returns a DateTimeOffset representing the current date and time. The
+ // resolution of the returned value depends on the system timer. For
+ // Windows NT 3.5 and later the timer resolution is approximately 10ms,
+ // for Windows NT 3.1 it is approximately 16ms, and for Windows 95 and 98
+ // it is approximately 55ms.
+ //
+ public static DateTimeOffset Now
+ {
+ get
+ {
+ return new DateTimeOffset(DateTime.Now);
+ }
+ }
+
+ public static DateTimeOffset UtcNow
+ {
+ get
+ {
+ return new DateTimeOffset(DateTime.UtcNow);
+ }
+ }
+
+ public DateTime DateTime
+ {
+ get
+ {
+ return ClockDateTime;
+ }
+ }
+
+ public DateTime UtcDateTime
+ {
+ get
+ {
+ return DateTime.SpecifyKind(_dateTime, DateTimeKind.Utc);
+ }
+ }
+
+ public DateTime LocalDateTime
+ {
+ get
+ {
+ return UtcDateTime.ToLocalTime();
+ }
+ }
+
+ // Adjust to a given offset with the same UTC time. Can throw ArgumentException
+ //
+ public DateTimeOffset ToOffset(TimeSpan offset)
+ {
+ return new DateTimeOffset((_dateTime + offset).Ticks, offset);
+ }
+
+
+ // Instance Properties
+
+ // The clock or visible time represented. This is just a wrapper around the internal date because this is
+ // the chosen storage mechanism. Going through this helper is good for readability and maintainability.
+ // This should be used for display but not identity.
+ private DateTime ClockDateTime
+ {
+ get
+ {
+ return new DateTime((_dateTime + Offset).Ticks, DateTimeKind.Unspecified);
+ }
+ }
+
+ // Returns the date part of this DateTimeOffset. The resulting value
+ // corresponds to this DateTimeOffset with the time-of-day part set to
+ // zero (midnight).
+ //
+ public DateTime Date
+ {
+ get
+ {
+ return ClockDateTime.Date;
+ }
+ }
+
+ // Returns the day-of-month part of this DateTimeOffset. The returned
+ // value is an integer between 1 and 31.
+ //
+ public int Day
+ {
+ get
+ {
+ return ClockDateTime.Day;
+ }
+ }
+
+ // Returns the day-of-week part of this DateTimeOffset. The returned value
+ // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
+ // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
+ // Thursday, 5 indicates Friday, and 6 indicates Saturday.
+ //
+ public DayOfWeek DayOfWeek
+ {
+ get
+ {
+ return ClockDateTime.DayOfWeek;
+ }
+ }
+
+ // Returns the day-of-year part of this DateTimeOffset. The returned value
+ // is an integer between 1 and 366.
+ //
+ public int DayOfYear
+ {
+ get
+ {
+ return ClockDateTime.DayOfYear;
+ }
+ }
+
+ // Returns the hour part of this DateTimeOffset. The returned value is an
+ // integer between 0 and 23.
+ //
+ public int Hour
+ {
+ get
+ {
+ return ClockDateTime.Hour;
+ }
+ }
+
+
+ // Returns the millisecond part of this DateTimeOffset. The returned value
+ // is an integer between 0 and 999.
+ //
+ public int Millisecond
+ {
+ get
+ {
+ return ClockDateTime.Millisecond;
+ }
+ }
+
+ // Returns the minute part of this DateTimeOffset. The returned value is
+ // an integer between 0 and 59.
+ //
+ public int Minute
+ {
+ get
+ {
+ return ClockDateTime.Minute;
+ }
+ }
+
+ // Returns the month part of this DateTimeOffset. The returned value is an
+ // integer between 1 and 12.
+ //
+ public int Month
+ {
+ get
+ {
+ return ClockDateTime.Month;
+ }
+ }
+
+ public TimeSpan Offset
+ {
+ get
+ {
+ return new TimeSpan(0, _offsetMinutes, 0);
+ }
+ }
+
+ // Returns the second part of this DateTimeOffset. The returned value is
+ // an integer between 0 and 59.
+ //
+ public int Second
+ {
+ get
+ {
+ return ClockDateTime.Second;
+ }
+ }
+
+ // Returns the tick count for this DateTimeOffset. The returned value is
+ // the number of 100-nanosecond intervals that have elapsed since 1/1/0001
+ // 12:00am.
+ //
+ public long Ticks
+ {
+ get
+ {
+ return ClockDateTime.Ticks;
+ }
+ }
+
+ public long UtcTicks
+ {
+ get
+ {
+ return UtcDateTime.Ticks;
+ }
+ }
+
+ // Returns the time-of-day part of this DateTimeOffset. The returned value
+ // is a TimeSpan that indicates the time elapsed since midnight.
+ //
+ public TimeSpan TimeOfDay
+ {
+ get
+ {
+ return ClockDateTime.TimeOfDay;
+ }
+ }
+
+ // Returns the year part of this DateTimeOffset. The returned value is an
+ // integer between 1 and 9999.
+ //
+ public int Year
+ {
+ get
+ {
+ return ClockDateTime.Year;
+ }
+ }
+
+ // Returns the DateTimeOffset resulting from adding the given
+ // TimeSpan to this DateTimeOffset.
+ //
+ public DateTimeOffset Add(TimeSpan timeSpan)
+ {
+ return new DateTimeOffset(ClockDateTime.Add(timeSpan), Offset);
+ }
+
+ // Returns the DateTimeOffset resulting from adding a fractional number of
+ // days to this DateTimeOffset. The result is computed by rounding the
+ // fractional number of days given by value to the nearest
+ // millisecond, and adding that interval to this DateTimeOffset. The
+ // value argument is permitted to be negative.
+ //
+ public DateTimeOffset AddDays(double days)
+ {
+ return new DateTimeOffset(ClockDateTime.AddDays(days), Offset);
+ }
+
+ // Returns the DateTimeOffset resulting from adding a fractional number of
+ // hours to this DateTimeOffset. The result is computed by rounding the
+ // fractional number of hours given by value to the nearest
+ // millisecond, and adding that interval to this DateTimeOffset. The
+ // value argument is permitted to be negative.
+ //
+ public DateTimeOffset AddHours(double hours)
+ {
+ return new DateTimeOffset(ClockDateTime.AddHours(hours), Offset);
+ }
+
+ // Returns the DateTimeOffset resulting from the given number of
+ // milliseconds to this DateTimeOffset. The result is computed by rounding
+ // the number of milliseconds given by value to the nearest integer,
+ // and adding that interval to this DateTimeOffset. The value
+ // argument is permitted to be negative.
+ //
+ public DateTimeOffset AddMilliseconds(double milliseconds)
+ {
+ return new DateTimeOffset(ClockDateTime.AddMilliseconds(milliseconds), Offset);
+ }
+
+ // Returns the DateTimeOffset resulting from adding a fractional number of
+ // minutes to this DateTimeOffset. The result is computed by rounding the
+ // fractional number of minutes given by value to the nearest
+ // millisecond, and adding that interval to this DateTimeOffset. The
+ // value argument is permitted to be negative.
+ //
+ public DateTimeOffset AddMinutes(double minutes)
+ {
+ return new DateTimeOffset(ClockDateTime.AddMinutes(minutes), Offset);
+ }
+
+ public DateTimeOffset AddMonths(int months)
+ {
+ return new DateTimeOffset(ClockDateTime.AddMonths(months), Offset);
+ }
+
+ // Returns the DateTimeOffset resulting from adding a fractional number of
+ // seconds to this DateTimeOffset. The result is computed by rounding the
+ // fractional number of seconds given by value to the nearest
+ // millisecond, and adding that interval to this DateTimeOffset. The
+ // value argument is permitted to be negative.
+ //
+ public DateTimeOffset AddSeconds(double seconds)
+ {
+ return new DateTimeOffset(ClockDateTime.AddSeconds(seconds), Offset);
+ }
+
+ // Returns the DateTimeOffset resulting from adding the given number of
+ // 100-nanosecond ticks to this DateTimeOffset. The value argument
+ // is permitted to be negative.
+ //
+ public DateTimeOffset AddTicks(long ticks)
+ {
+ return new DateTimeOffset(ClockDateTime.AddTicks(ticks), Offset);
+ }
+
+ // Returns the DateTimeOffset resulting from adding the given number of
+ // years to this DateTimeOffset. The result is computed by incrementing
+ // (or decrementing) the year part of this DateTimeOffset by value
+ // years. If the month and day of this DateTimeOffset is 2/29, and if the
+ // resulting year is not a leap year, the month and day of the resulting
+ // DateTimeOffset becomes 2/28. Otherwise, the month, day, and time-of-day
+ // parts of the result are the same as those of this DateTimeOffset.
+ //
+ public DateTimeOffset AddYears(int years)
+ {
+ return new DateTimeOffset(ClockDateTime.AddYears(years), Offset);
+ }
+
+ // Compares two DateTimeOffset values, returning an integer that indicates
+ // their relationship.
+ //
+ public static int Compare(DateTimeOffset first, DateTimeOffset second)
+ {
+ return DateTime.Compare(first.UtcDateTime, second.UtcDateTime);
+ }
+
+ // Compares this DateTimeOffset to a given object. This method provides an
+ // implementation of the IComparable interface. The object
+ // argument must be another DateTimeOffset, or otherwise an exception
+ // occurs. Null is considered less than any instance.
+ //
+ int IComparable.CompareTo(Object obj)
+ {
+ if (obj == null) return 1;
+ if (!(obj is DateTimeOffset))
+ {
+ throw new ArgumentException(SR.Arg_MustBeDateTimeOffset);
+ }
+
+ DateTime objUtc = ((DateTimeOffset)obj).UtcDateTime;
+ DateTime utc = UtcDateTime;
+ if (utc > objUtc) return 1;
+ if (utc < objUtc) return -1;
+ return 0;
+ }
+
+ public int CompareTo(DateTimeOffset other)
+ {
+ DateTime otherUtc = other.UtcDateTime;
+ DateTime utc = UtcDateTime;
+ if (utc > otherUtc) return 1;
+ if (utc < otherUtc) return -1;
+ return 0;
+ }
+
+
+ // Checks if this DateTimeOffset is equal to a given object. Returns
+ // true if the given object is a boxed DateTimeOffset and its value
+ // is equal to the value of this DateTimeOffset. Returns false
+ // otherwise.
+ //
+ public override bool Equals(Object obj)
+ {
+ if (obj is DateTimeOffset)
+ {
+ return UtcDateTime.Equals(((DateTimeOffset)obj).UtcDateTime);
+ }
+ return false;
+ }
+
+ public bool Equals(DateTimeOffset other)
+ {
+ return UtcDateTime.Equals(other.UtcDateTime);
+ }
+
+ public bool EqualsExact(DateTimeOffset other)
+ {
+ //
+ // returns true when the ClockDateTime, Kind, and Offset match
+ //
+ // currently the Kind should always be Unspecified, but there is always the possibility that a future version
+ // of DateTimeOffset overloads the Kind field
+ //
+ return (ClockDateTime == other.ClockDateTime && Offset == other.Offset && ClockDateTime.Kind == other.ClockDateTime.Kind);
+ }
+
+ // Compares two DateTimeOffset values for equality. Returns true if
+ // the two DateTimeOffset values are equal, or false if they are
+ // not equal.
+ //
+ public static bool Equals(DateTimeOffset first, DateTimeOffset second)
+ {
+ return DateTime.Equals(first.UtcDateTime, second.UtcDateTime);
+ }
+
+ // Creates a DateTimeOffset from a Windows filetime. A Windows filetime is
+ // a long representing the date and time as the number of
+ // 100-nanosecond intervals that have elapsed since 1/1/1601 12:00am.
+ //
+ public static DateTimeOffset FromFileTime(long fileTime)
+ {
+ return new DateTimeOffset(DateTime.FromFileTime(fileTime));
+ }
+
+ public static DateTimeOffset FromUnixTimeSeconds(long seconds)
+ {
+ if (seconds < UnixMinSeconds || seconds > UnixMaxSeconds)
+ {
+ throw new ArgumentOutOfRangeException(nameof(seconds),
+ SR.Format(SR.ArgumentOutOfRange_Range, UnixMinSeconds, UnixMaxSeconds));
+ }
+
+ long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks;
+ return new DateTimeOffset(ticks, TimeSpan.Zero);
+ }
+
+ public static DateTimeOffset FromUnixTimeMilliseconds(long milliseconds)
+ {
+ const long MinMilliseconds = DateTime.MinTicks / TimeSpan.TicksPerMillisecond - UnixEpochMilliseconds;
+ const long MaxMilliseconds = DateTime.MaxTicks / TimeSpan.TicksPerMillisecond - UnixEpochMilliseconds;
+
+ if (milliseconds < MinMilliseconds || milliseconds > MaxMilliseconds)
+ {
+ throw new ArgumentOutOfRangeException(nameof(milliseconds),
+ SR.Format(SR.ArgumentOutOfRange_Range, MinMilliseconds, MaxMilliseconds));
+ }
+
+ long ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks;
+ return new DateTimeOffset(ticks, TimeSpan.Zero);
+ }
+
+ // ----- SECTION: private serialization instance methods ----------------*
+
+ void IDeserializationCallback.OnDeserialization(Object sender)
+ {
+ try
+ {
+ _offsetMinutes = ValidateOffset(Offset);
+ _dateTime = ValidateDate(ClockDateTime, Offset);
+ }
+ catch (ArgumentException e)
+ {
+ throw new SerializationException(SR.Serialization_InvalidData, e);
+ }
+ }
+
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ info.AddValue("DateTime", _dateTime);
+ info.AddValue("OffsetMinutes", _offsetMinutes);
+ }
+
+
+ private DateTimeOffset(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+
+ _dateTime = (DateTime)info.GetValue("DateTime", typeof(DateTime));
+ _offsetMinutes = (Int16)info.GetValue("OffsetMinutes", typeof(Int16));
+ }
+
+ // Returns the hash code for this DateTimeOffset.
+ //
+ public override int GetHashCode()
+ {
+ return UtcDateTime.GetHashCode();
+ }
+
+ // Constructs a DateTimeOffset from a string. The string must specify a
+ // date and optionally a time in a culture-specific or universal format.
+ // Leading and trailing whitespace characters are allowed.
+ //
+ public static DateTimeOffset Parse(String input)
+ {
+ TimeSpan offset;
+ DateTime dateResult = DateTimeParse.Parse(input,
+ DateTimeFormatInfo.CurrentInfo,
+ DateTimeStyles.None,
+ out offset);
+ return new DateTimeOffset(dateResult.Ticks, offset);
+ }
+
+ // Constructs a DateTimeOffset from a string. The string must specify a
+ // date and optionally a time in a culture-specific or universal format.
+ // Leading and trailing whitespace characters are allowed.
+ //
+ public static DateTimeOffset Parse(String input, IFormatProvider formatProvider)
+ {
+ return Parse(input, formatProvider, DateTimeStyles.None);
+ }
+
+ public static DateTimeOffset Parse(String input, IFormatProvider formatProvider, DateTimeStyles styles)
+ {
+ styles = ValidateStyles(styles, nameof(styles));
+ TimeSpan offset;
+ DateTime dateResult = DateTimeParse.Parse(input,
+ DateTimeFormatInfo.GetInstance(formatProvider),
+ styles,
+ out offset);
+ return new DateTimeOffset(dateResult.Ticks, offset);
+ }
+
+ // Constructs a DateTimeOffset from a string. The string must specify a
+ // date and optionally a time in a culture-specific or universal format.
+ // Leading and trailing whitespace characters are allowed.
+ //
+ public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider)
+ {
+ return ParseExact(input, format, formatProvider, DateTimeStyles.None);
+ }
+
+ // Constructs a DateTimeOffset from a string. The string must specify a
+ // date and optionally a time in a culture-specific or universal format.
+ // Leading and trailing whitespace characters are allowed.
+ //
+ public static DateTimeOffset ParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles)
+ {
+ styles = ValidateStyles(styles, nameof(styles));
+ TimeSpan offset;
+ DateTime dateResult = DateTimeParse.ParseExact(input,
+ format,
+ DateTimeFormatInfo.GetInstance(formatProvider),
+ styles,
+ out offset);
+ return new DateTimeOffset(dateResult.Ticks, offset);
+ }
+
+ public static DateTimeOffset ParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles)
+ {
+ styles = ValidateStyles(styles, nameof(styles));
+ TimeSpan offset;
+ DateTime dateResult = DateTimeParse.ParseExactMultiple(input,
+ formats,
+ DateTimeFormatInfo.GetInstance(formatProvider),
+ styles,
+ out offset);
+ return new DateTimeOffset(dateResult.Ticks, offset);
+ }
+
+ public TimeSpan Subtract(DateTimeOffset value)
+ {
+ return UtcDateTime.Subtract(value.UtcDateTime);
+ }
+
+ public DateTimeOffset Subtract(TimeSpan value)
+ {
+ return new DateTimeOffset(ClockDateTime.Subtract(value), Offset);
+ }
+
+
+ public long ToFileTime()
+ {
+ return UtcDateTime.ToFileTime();
+ }
+
+ public long ToUnixTimeSeconds()
+ {
+ // Truncate sub-second precision before offsetting by the Unix Epoch to avoid
+ // the last digit being off by one for dates that result in negative Unix times.
+ //
+ // For example, consider the DateTimeOffset 12/31/1969 12:59:59.001 +0
+ // ticks = 621355967990010000
+ // ticksFromEpoch = ticks - UnixEpochTicks = -9990000
+ // secondsFromEpoch = ticksFromEpoch / TimeSpan.TicksPerSecond = 0
+ //
+ // Notice that secondsFromEpoch is rounded *up* by the truncation induced by integer division,
+ // whereas we actually always want to round *down* when converting to Unix time. This happens
+ // automatically for positive Unix time values. Now the example becomes:
+ // seconds = ticks / TimeSpan.TicksPerSecond = 62135596799
+ // secondsFromEpoch = seconds - UnixEpochSeconds = -1
+ //
+ // In other words, we want to consistently round toward the time 1/1/0001 00:00:00,
+ // rather than toward the Unix Epoch (1/1/1970 00:00:00).
+ long seconds = UtcDateTime.Ticks / TimeSpan.TicksPerSecond;
+ return seconds - UnixEpochSeconds;
+ }
+
+ public long ToUnixTimeMilliseconds()
+ {
+ // Truncate sub-millisecond precision before offsetting by the Unix Epoch to avoid
+ // the last digit being off by one for dates that result in negative Unix times
+ long milliseconds = UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond;
+ return milliseconds - UnixEpochMilliseconds;
+ }
+
+ public DateTimeOffset ToLocalTime()
+ {
+ return ToLocalTime(false);
+ }
+
+ internal DateTimeOffset ToLocalTime(bool throwOnOverflow)
+ {
+ return new DateTimeOffset(UtcDateTime.ToLocalTime(throwOnOverflow));
+ }
+
+ public override String ToString()
+ {
+ return DateTimeFormat.Format(ClockDateTime, null, DateTimeFormatInfo.CurrentInfo, Offset);
+ }
+
+ public String ToString(String format)
+ {
+ return DateTimeFormat.Format(ClockDateTime, format, DateTimeFormatInfo.CurrentInfo, Offset);
+ }
+
+ public String ToString(IFormatProvider formatProvider)
+ {
+ return DateTimeFormat.Format(ClockDateTime, null, DateTimeFormatInfo.GetInstance(formatProvider), Offset);
+ }
+
+ public String ToString(String format, IFormatProvider formatProvider)
+ {
+ return DateTimeFormat.Format(ClockDateTime, format, DateTimeFormatInfo.GetInstance(formatProvider), Offset);
+ }
+
+ public DateTimeOffset ToUniversalTime()
+ {
+ return new DateTimeOffset(UtcDateTime);
+ }
+
+ public static Boolean TryParse(String input, out DateTimeOffset result)
+ {
+ TimeSpan offset;
+ DateTime dateResult;
+ Boolean parsed = DateTimeParse.TryParse(input,
+ DateTimeFormatInfo.CurrentInfo,
+ DateTimeStyles.None,
+ out dateResult,
+ out offset);
+ result = new DateTimeOffset(dateResult.Ticks, offset);
+ return parsed;
+ }
+
+ public static Boolean TryParse(String input, IFormatProvider formatProvider, DateTimeStyles styles, out DateTimeOffset result)
+ {
+ styles = ValidateStyles(styles, nameof(styles));
+ TimeSpan offset;
+ DateTime dateResult;
+ Boolean parsed = DateTimeParse.TryParse(input,
+ DateTimeFormatInfo.GetInstance(formatProvider),
+ styles,
+ out dateResult,
+ out offset);
+ result = new DateTimeOffset(dateResult.Ticks, offset);
+ return parsed;
+ }
+
+ public static Boolean TryParseExact(String input, String format, IFormatProvider formatProvider, DateTimeStyles styles,
+ out DateTimeOffset result)
+ {
+ styles = ValidateStyles(styles, nameof(styles));
+ TimeSpan offset;
+ DateTime dateResult;
+ Boolean parsed = DateTimeParse.TryParseExact(input,
+ format,
+ DateTimeFormatInfo.GetInstance(formatProvider),
+ styles,
+ out dateResult,
+ out offset);
+ result = new DateTimeOffset(dateResult.Ticks, offset);
+ return parsed;
+ }
+
+ public static Boolean TryParseExact(String input, String[] formats, IFormatProvider formatProvider, DateTimeStyles styles,
+ out DateTimeOffset result)
+ {
+ styles = ValidateStyles(styles, nameof(styles));
+ TimeSpan offset;
+ DateTime dateResult;
+ Boolean parsed = DateTimeParse.TryParseExactMultiple(input,
+ formats,
+ DateTimeFormatInfo.GetInstance(formatProvider),
+ styles,
+ out dateResult,
+ out offset);
+ result = new DateTimeOffset(dateResult.Ticks, offset);
+ return parsed;
+ }
+
+ // Ensures the TimeSpan is valid to go in a DateTimeOffset.
+ private static Int16 ValidateOffset(TimeSpan offset)
+ {
+ Int64 ticks = offset.Ticks;
+ if (ticks % TimeSpan.TicksPerMinute != 0)
+ {
+ throw new ArgumentException(SR.Argument_OffsetPrecision, nameof(offset));
+ }
+ if (ticks < MinOffset || ticks > MaxOffset)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.Argument_OffsetOutOfRange);
+ }
+ return (Int16)(offset.Ticks / TimeSpan.TicksPerMinute);
+ }
+
+ // Ensures that the time and offset are in range.
+ private static DateTime ValidateDate(DateTime dateTime, TimeSpan offset)
+ {
+ // The key validation is that both the UTC and clock times fit. The clock time is validated
+ // by the DateTime constructor.
+ Debug.Assert(offset.Ticks >= MinOffset && offset.Ticks <= MaxOffset, "Offset not validated.");
+
+ // This operation cannot overflow because offset should have already been validated to be within
+ // 14 hours and the DateTime instance is more than that distance from the boundaries of Int64.
+ Int64 utcTicks = dateTime.Ticks - offset.Ticks;
+ if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.Argument_UTCOutOfRange);
+ }
+ // make sure the Kind is set to Unspecified
+ //
+ return new DateTime(utcTicks, DateTimeKind.Unspecified);
+ }
+
+ private static DateTimeStyles ValidateStyles(DateTimeStyles style, String parameterName)
+ {
+ if ((style & DateTimeFormatInfo.InvalidDateTimeStyles) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateTimeStyles, parameterName);
+ }
+ if (((style & (DateTimeStyles.AssumeLocal)) != 0) && ((style & (DateTimeStyles.AssumeUniversal)) != 0))
+ {
+ throw new ArgumentException(SR.Argument_ConflictingDateTimeStyles, parameterName);
+ }
+ if ((style & DateTimeStyles.NoCurrentDateDefault) != 0)
+ {
+ throw new ArgumentException(SR.Argument_DateTimeOffsetInvalidDateTimeStyles, parameterName);
+ }
+
+ // RoundtripKind does not make sense for DateTimeOffset; ignore this flag for backward compatibility with DateTime
+ style &= ~DateTimeStyles.RoundtripKind;
+
+ // AssumeLocal is also ignored as that is what we do by default with DateTimeOffset.Parse
+ style &= ~DateTimeStyles.AssumeLocal;
+
+ return style;
+ }
+
+ // Operators
+
+ public static implicit operator DateTimeOffset(DateTime dateTime)
+ {
+ return new DateTimeOffset(dateTime);
+ }
+
+ public static DateTimeOffset operator +(DateTimeOffset dateTimeOffset, TimeSpan timeSpan)
+ {
+ return new DateTimeOffset(dateTimeOffset.ClockDateTime + timeSpan, dateTimeOffset.Offset);
+ }
+
+
+ public static DateTimeOffset operator -(DateTimeOffset dateTimeOffset, TimeSpan timeSpan)
+ {
+ return new DateTimeOffset(dateTimeOffset.ClockDateTime - timeSpan, dateTimeOffset.Offset);
+ }
+
+ public static TimeSpan operator -(DateTimeOffset left, DateTimeOffset right)
+ {
+ return left.UtcDateTime - right.UtcDateTime;
+ }
+
+ public static bool operator ==(DateTimeOffset left, DateTimeOffset right)
+ {
+ return left.UtcDateTime == right.UtcDateTime;
+ }
+
+ public static bool operator !=(DateTimeOffset left, DateTimeOffset right)
+ {
+ return left.UtcDateTime != right.UtcDateTime;
+ }
+
+ public static bool operator <(DateTimeOffset left, DateTimeOffset right)
+ {
+ return left.UtcDateTime < right.UtcDateTime;
+ }
+
+ public static bool operator <=(DateTimeOffset left, DateTimeOffset right)
+ {
+ return left.UtcDateTime <= right.UtcDateTime;
+ }
+
+ public static bool operator >(DateTimeOffset left, DateTimeOffset right)
+ {
+ return left.UtcDateTime > right.UtcDateTime;
+ }
+
+ public static bool operator >=(DateTimeOffset left, DateTimeOffset right)
+ {
+ return left.UtcDateTime >= right.UtcDateTime;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/DayOfWeek.cs b/src/mscorlib/shared/System/DayOfWeek.cs
new file mode 100644
index 0000000000..5d84257158
--- /dev/null
+++ b/src/mscorlib/shared/System/DayOfWeek.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Enum for the day of the week.
+**
+**
+============================================================*/
+
+namespace System
+{
+ [Serializable]
+ public enum DayOfWeek
+ {
+ Sunday = 0,
+ Monday = 1,
+ Tuesday = 2,
+ Wednesday = 3,
+ Thursday = 4,
+ Friday = 5,
+ Saturday = 6,
+ }
+}
diff --git a/src/mscorlib/shared/System/DefaultBinder.cs b/src/mscorlib/shared/System/DefaultBinder.cs
new file mode 100644
index 0000000000..3b46d5f4d3
--- /dev/null
+++ b/src/mscorlib/shared/System/DefaultBinder.cs
@@ -0,0 +1,1201 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Reflection;
+using System.Diagnostics;
+using CultureInfo = System.Globalization.CultureInfo;
+
+namespace System
+{
+ //Marked serializable even though it has no state.
+ [Serializable]
+#if CORECLR
+ internal
+#else
+ public sealed
+#endif
+ partial class DefaultBinder : Binder
+ {
+ // This method is passed a set of methods and must choose the best
+ // fit. The methods all have the same number of arguments and the object
+ // array args. On exit, this method will choice the best fit method
+ // and coerce the args to match that method. By match, we mean all primitive
+ // arguments are exact matchs and all object arguments are exact or subclasses
+ // of the target. If the target OR is an interface, the object must implement
+ // that interface. There are a couple of exceptions
+ // thrown when a method cannot be returned. If no method matchs the args and
+ // ArgumentException is thrown. If multiple methods match the args then
+ // an AmbiguousMatchException is thrown.
+ //
+ // The most specific match will be selected.
+ //
+ public sealed override MethodBase BindToMethod(
+ BindingFlags bindingAttr, MethodBase[] match, ref object[] args,
+ ParameterModifier[] modifiers, CultureInfo cultureInfo, string[] names, out object state)
+ {
+ if (match == null || match.Length == 0)
+ throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
+
+ MethodBase[] candidates = (MethodBase[])match.Clone();
+
+ int i;
+ int j;
+
+ state = null;
+
+#region Map named parameters to candidate parameter postions
+ // We are creating an paramOrder array to act as a mapping
+ // between the order of the args and the actual order of the
+ // parameters in the method. This order may differ because
+ // named parameters (names) may change the order. If names
+ // is not provided, then we assume the default mapping (0,1,...)
+ int[][] paramOrder = new int[candidates.Length][];
+
+ for (i = 0; i < candidates.Length; i++)
+ {
+ ParameterInfo[] par = candidates[i].GetParametersNoCopy();
+
+ // args.Length + 1 takes into account the possibility of a last paramArray that can be omitted
+ paramOrder[i] = new int[(par.Length > args.Length) ? par.Length : args.Length];
+
+ if (names == null)
+ {
+ // Default mapping
+ for (j = 0; j < args.Length; j++)
+ paramOrder[i][j] = j;
+ }
+ else
+ {
+ // Named parameters, reorder the mapping. If CreateParamOrder fails, it means that the method
+ // doesn't have a name that matchs one of the named parameters so we don't consider it any further.
+ if (!CreateParamOrder(paramOrder[i], par, names))
+ candidates[i] = null;
+ }
+ }
+#endregion
+
+ Type[] paramArrayTypes = new Type[candidates.Length];
+
+ Type[] argTypes = new Type[args.Length];
+
+#region Cache the type of the provided arguments
+ // object that contain a null are treated as if they were typeless (but match either object
+ // references or value classes). We mark this condition by placing a null in the argTypes array.
+ for (i = 0; i < args.Length; i++)
+ {
+ if (args[i] != null)
+ {
+ argTypes[i] = args[i].GetType();
+ }
+ }
+#endregion
+
+
+ // Find the method that matches...
+ int CurIdx = 0;
+ bool defaultValueBinding = ((bindingAttr & BindingFlags.OptionalParamBinding) != 0);
+
+ Type paramArrayType = null;
+
+#region Filter methods by parameter count and type
+ for (i = 0; i < candidates.Length; i++)
+ {
+ paramArrayType = null;
+
+ // If we have named parameters then we may have a hole in the candidates array.
+ if (candidates[i] == null)
+ continue;
+
+ // Validate the parameters.
+ ParameterInfo[] par = candidates[i].GetParametersNoCopy();
+
+#region Match method by parameter count
+ if (par.Length == 0)
+ {
+#region No formal parameters
+ if (args.Length != 0)
+ {
+ if ((candidates[i].CallingConvention & CallingConventions.VarArgs) == 0)
+ continue;
+ }
+
+ // This is a valid routine so we move it up the candidates list.
+ paramOrder[CurIdx] = paramOrder[i];
+ candidates[CurIdx++] = candidates[i];
+
+ continue;
+#endregion
+ }
+ else if (par.Length > args.Length)
+ {
+#region Shortage of provided parameters
+ // If the number of parameters is greater than the number of args then
+ // we are in the situation were we may be using default values.
+ for (j = args.Length; j < par.Length - 1; j++)
+ {
+ if (par[j].DefaultValue == System.DBNull.Value)
+ break;
+ }
+
+ if (j != par.Length - 1)
+ continue;
+
+ if (par[j].DefaultValue == System.DBNull.Value)
+ {
+ if (!par[j].ParameterType.IsArray)
+ continue;
+
+ if (!par[j].IsDefined(typeof(ParamArrayAttribute), true))
+ continue;
+
+ paramArrayType = par[j].ParameterType.GetElementType();
+ }
+#endregion
+ }
+ else if (par.Length < args.Length)
+ {
+#region Excess provided parameters
+ // test for the ParamArray case
+ int lastArgPos = par.Length - 1;
+
+ if (!par[lastArgPos].ParameterType.IsArray)
+ continue;
+
+ if (!par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true))
+ continue;
+
+ if (paramOrder[i][lastArgPos] != lastArgPos)
+ continue;
+
+ paramArrayType = par[lastArgPos].ParameterType.GetElementType();
+#endregion
+ }
+ else
+ {
+#region Test for paramArray, save paramArray type
+ int lastArgPos = par.Length - 1;
+
+ if (par[lastArgPos].ParameterType.IsArray
+ && par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true)
+ && paramOrder[i][lastArgPos] == lastArgPos)
+ {
+ if (!par[lastArgPos].ParameterType.IsAssignableFrom(argTypes[lastArgPos]))
+ paramArrayType = par[lastArgPos].ParameterType.GetElementType();
+ }
+#endregion
+ }
+#endregion
+
+ Type pCls = null;
+ int argsToCheck = (paramArrayType != null) ? par.Length - 1 : args.Length;
+
+#region Match method by parameter type
+ for (j = 0; j < argsToCheck; j++)
+ {
+#region Classic argument coersion checks
+ // get the formal type
+ pCls = par[j].ParameterType;
+
+ if (pCls.IsByRef)
+ pCls = pCls.GetElementType();
+
+ // the type is the same
+ if (pCls == argTypes[paramOrder[i][j]])
+ continue;
+
+ // a default value is available
+ if (defaultValueBinding && args[paramOrder[i][j]] == Type.Missing)
+ continue;
+
+ // the argument was null, so it matches with everything
+ if (args[paramOrder[i][j]] == null)
+ continue;
+
+ // the type is Object, so it will match everything
+ if (pCls == typeof(object))
+ continue;
+
+ // now do a "classic" type check
+ if (pCls.IsPrimitive)
+ {
+ if (argTypes[paramOrder[i][j]] == null || !CanChangePrimitiveObjectToType(args[paramOrder[i][j]], pCls))
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (argTypes[paramOrder[i][j]] == null)
+ continue;
+
+ if (!pCls.IsAssignableFrom(argTypes[paramOrder[i][j]]))
+ {
+ if (argTypes[paramOrder[i][j]].IsCOMObject)
+ {
+ if (pCls.IsInstanceOfType(args[paramOrder[i][j]]))
+ continue;
+ }
+ break;
+ }
+ }
+#endregion
+ }
+
+ if (paramArrayType != null && j == par.Length - 1)
+ {
+#region Check that excess arguments can be placed in the param array
+ for (; j < args.Length; j++)
+ {
+ if (paramArrayType.IsPrimitive)
+ {
+ if (argTypes[j] == null || !CanChangePrimitiveObjectToType(args[j], paramArrayType))
+ break;
+ }
+ else
+ {
+ if (argTypes[j] == null)
+ continue;
+
+ if (!paramArrayType.IsAssignableFrom(argTypes[j]))
+ {
+ if (argTypes[j].IsCOMObject)
+ {
+ if (paramArrayType.IsInstanceOfType(args[j]))
+ continue;
+ }
+
+ break;
+ }
+ }
+ }
+#endregion
+ }
+#endregion
+
+ if (j == args.Length)
+ {
+#region This is a valid routine so we move it up the candidates list
+ paramOrder[CurIdx] = paramOrder[i];
+ paramArrayTypes[CurIdx] = paramArrayType;
+ candidates[CurIdx++] = candidates[i];
+#endregion
+ }
+ }
+#endregion
+
+ // If we didn't find a method
+ if (CurIdx == 0)
+ throw new MissingMethodException(SR.MissingMember);
+
+ if (CurIdx == 1)
+ {
+#region Found only one method
+ if (names != null)
+ {
+ state = new BinderState((int[])paramOrder[0].Clone(), args.Length, paramArrayTypes[0] != null);
+ ReorderParams(paramOrder[0], args);
+ }
+
+ // If the parameters and the args are not the same length or there is a paramArray
+ // then we need to create a argument array.
+ ParameterInfo[] parms = candidates[0].GetParametersNoCopy();
+
+ if (parms.Length == args.Length)
+ {
+ if (paramArrayTypes[0] != null)
+ {
+ object[] objs = new object[parms.Length];
+ int lastPos = parms.Length - 1;
+ Array.Copy(args, 0, objs, 0, lastPos);
+ objs[lastPos] = Array.CreateInstance(paramArrayTypes[0], 1);
+ ((Array)objs[lastPos]).SetValue(args[lastPos], 0);
+ args = objs;
+ }
+ }
+ else if (parms.Length > args.Length)
+ {
+ object[] objs = new object[parms.Length];
+
+ for (i = 0; i < args.Length; i++)
+ objs[i] = args[i];
+
+ for (; i < parms.Length - 1; i++)
+ objs[i] = parms[i].DefaultValue;
+
+ if (paramArrayTypes[0] != null)
+ objs[i] = Array.CreateInstance(paramArrayTypes[0], 0); // create an empty array for the
+
+ else
+ objs[i] = parms[i].DefaultValue;
+
+ args = objs;
+ }
+ else
+ {
+ if ((candidates[0].CallingConvention & CallingConventions.VarArgs) == 0)
+ {
+ object[] objs = new object[parms.Length];
+ int paramArrayPos = parms.Length - 1;
+ Array.Copy(args, 0, objs, 0, paramArrayPos);
+ objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[0], args.Length - paramArrayPos);
+ Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos);
+ args = objs;
+ }
+ }
+#endregion
+
+ return candidates[0];
+ }
+
+ int currentMin = 0;
+ bool ambig = false;
+ for (i = 1; i < CurIdx; i++)
+ {
+#region Walk all of the methods looking the most specific method to invoke
+ int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder[currentMin], paramArrayTypes[currentMin],
+ candidates[i], paramOrder[i], paramArrayTypes[i], argTypes, args);
+
+ if (newMin == 0)
+ {
+ ambig = true;
+ }
+ else if (newMin == 2)
+ {
+ currentMin = i;
+ ambig = false;
+ }
+#endregion
+ }
+
+ if (ambig)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+
+ // Reorder (if needed)
+ if (names != null)
+ {
+ state = new BinderState((int[])paramOrder[currentMin].Clone(), args.Length, paramArrayTypes[currentMin] != null);
+ ReorderParams(paramOrder[currentMin], args);
+ }
+
+ // If the parameters and the args are not the same length or there is a paramArray
+ // then we need to create a argument array.
+ ParameterInfo[] parameters = candidates[currentMin].GetParametersNoCopy();
+ if (parameters.Length == args.Length)
+ {
+ if (paramArrayTypes[currentMin] != null)
+ {
+ object[] objs = new object[parameters.Length];
+ int lastPos = parameters.Length - 1;
+ Array.Copy(args, 0, objs, 0, lastPos);
+ objs[lastPos] = Array.CreateInstance(paramArrayTypes[currentMin], 1);
+ ((Array)objs[lastPos]).SetValue(args[lastPos], 0);
+ args = objs;
+ }
+ }
+ else if (parameters.Length > args.Length)
+ {
+ object[] objs = new object[parameters.Length];
+
+ for (i = 0; i < args.Length; i++)
+ objs[i] = args[i];
+
+ for (; i < parameters.Length - 1; i++)
+ objs[i] = parameters[i].DefaultValue;
+
+ if (paramArrayTypes[currentMin] != null)
+ {
+ objs[i] = Array.CreateInstance(paramArrayTypes[currentMin], 0);
+ }
+ else
+ {
+ objs[i] = parameters[i].DefaultValue;
+ }
+
+ args = objs;
+ }
+ else
+ {
+ if ((candidates[currentMin].CallingConvention & CallingConventions.VarArgs) == 0)
+ {
+ object[] objs = new object[parameters.Length];
+ int paramArrayPos = parameters.Length - 1;
+ Array.Copy(args, 0, objs, 0, paramArrayPos);
+ objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[currentMin], args.Length - paramArrayPos);
+ Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos);
+ args = objs;
+ }
+ }
+
+ return candidates[currentMin];
+ }
+
+
+ // Given a set of fields that match the base criteria, select a field.
+ // if value is null then we have no way to select a field
+ public sealed override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo cultureInfo)
+ {
+ if (match == null)
+ {
+ throw new ArgumentNullException(nameof(match));
+ }
+
+ int i;
+ // Find the method that match...
+ int CurIdx = 0;
+
+ Type valueType = null;
+
+ FieldInfo[] candidates = (FieldInfo[])match.Clone();
+
+ // If we are a FieldSet, then use the value's type to disambiguate
+ if ((bindingAttr & BindingFlags.SetField) != 0)
+ {
+ valueType = value.GetType();
+
+ for (i = 0; i < candidates.Length; i++)
+ {
+ Type pCls = candidates[i].FieldType;
+ if (pCls == valueType)
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ if (value == Empty.Value)
+ {
+ // the object passed in was null which would match any non primitive non value type
+ if (pCls.IsClass)
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ }
+ if (pCls == typeof(object))
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ if (pCls.IsPrimitive)
+ {
+ if (CanChangePrimitiveObjectToType(value, pCls))
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ }
+ else
+ {
+ if (pCls.IsAssignableFrom(valueType))
+ {
+ candidates[CurIdx++] = candidates[i];
+ continue;
+ }
+ }
+ }
+ if (CurIdx == 0)
+ throw new MissingFieldException(SR.MissingField);
+ if (CurIdx == 1)
+ return candidates[0];
+ }
+
+ // Walk all of the methods looking the most specific method to invoke
+ int currentMin = 0;
+ bool ambig = false;
+ for (i = 1; i < CurIdx; i++)
+ {
+ int newMin = FindMostSpecificField(candidates[currentMin], candidates[i]);
+ if (newMin == 0)
+ ambig = true;
+ else
+ {
+ if (newMin == 2)
+ {
+ currentMin = i;
+ ambig = false;
+ }
+ }
+ }
+ if (ambig)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+ return candidates[currentMin];
+ }
+
+ // Given a set of methods that match the base criteria, select a method based
+ // upon an array of types. This method should return null if no method matchs
+ // the criteria.
+ public sealed override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
+ {
+ int i;
+ int j;
+
+ Type[] realTypes = new Type[types.Length];
+ for (i = 0; i < types.Length; i++)
+ {
+ realTypes[i] = types[i].UnderlyingSystemType;
+ if (!(realTypes[i].IsRuntimeImplemented()))
+ throw new ArgumentException(SR.Arg_MustBeType, nameof(types));
+ }
+ types = realTypes;
+
+ // We don't automatically jump out on exact match.
+ if (match == null || match.Length == 0)
+ throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
+
+ MethodBase[] candidates = (MethodBase[])match.Clone();
+
+ // Find all the methods that can be described by the types parameter.
+ // Remove all of them that cannot.
+ int CurIdx = 0;
+ for (i = 0; i < candidates.Length; i++)
+ {
+ ParameterInfo[] par = candidates[i].GetParametersNoCopy();
+ if (par.Length != types.Length)
+ continue;
+ for (j = 0; j < types.Length; j++)
+ {
+ Type pCls = par[j].ParameterType;
+ if (pCls == types[j])
+ continue;
+ if (pCls == typeof(object))
+ continue;
+ if (pCls.IsPrimitive)
+ {
+ if (!(types[j].UnderlyingSystemType.IsRuntimeImplemented()) ||
+ !CanChangePrimitive(types[j].UnderlyingSystemType, pCls.UnderlyingSystemType))
+ break;
+ }
+ else
+ {
+ if (!pCls.IsAssignableFrom(types[j]))
+ break;
+ }
+ }
+ if (j == types.Length)
+ candidates[CurIdx++] = candidates[i];
+ }
+ if (CurIdx == 0)
+ return null;
+ if (CurIdx == 1)
+ return candidates[0];
+
+ // Walk all of the methods looking the most specific method to invoke
+ int currentMin = 0;
+ bool ambig = false;
+ int[] paramOrder = new int[types.Length];
+ for (i = 0; i < types.Length; i++)
+ paramOrder[i] = i;
+ for (i = 1; i < CurIdx; i++)
+ {
+ int newMin = FindMostSpecificMethod(candidates[currentMin], paramOrder, null, candidates[i], paramOrder, null, types, null);
+ if (newMin == 0)
+ ambig = true;
+ else
+ {
+ if (newMin == 2)
+ {
+ currentMin = i;
+ ambig = false;
+ currentMin = i;
+ }
+ }
+ }
+ if (ambig)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+ return candidates[currentMin];
+ }
+
+ // Given a set of properties that match the base criteria, select one.
+ public sealed override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType,
+ Type[] indexes, ParameterModifier[] modifiers)
+ {
+ // Allow a null indexes array. But if it is not null, every element must be non-null as well.
+ if (indexes != null)
+ {
+ foreach (Type index in indexes)
+ {
+ if (index == null)
+ throw new ArgumentNullException(nameof(indexes));
+ }
+ }
+
+ if (match == null || match.Length == 0)
+ throw new ArgumentException(SR.Arg_EmptyArray, nameof(match));
+
+ PropertyInfo[] candidates = (PropertyInfo[])match.Clone();
+
+ int i, j = 0;
+
+ // Find all the properties that can be described by type indexes parameter
+ int CurIdx = 0;
+ int indexesLength = (indexes != null) ? indexes.Length : 0;
+ for (i = 0; i < candidates.Length; i++)
+ {
+ if (indexes != null)
+ {
+ ParameterInfo[] par = candidates[i].GetIndexParameters();
+ if (par.Length != indexesLength)
+ continue;
+
+ for (j = 0; j < indexesLength; j++)
+ {
+ Type pCls = par[j].ParameterType;
+
+ // If the classes exactly match continue
+ if (pCls == indexes[j])
+ continue;
+ if (pCls == typeof(object))
+ continue;
+
+ if (pCls.IsPrimitive)
+ {
+ if (!(indexes[j].UnderlyingSystemType.IsRuntimeImplemented()) ||
+ !CanChangePrimitive(indexes[j].UnderlyingSystemType, pCls.UnderlyingSystemType))
+ break;
+ }
+ else
+ {
+ if (!pCls.IsAssignableFrom(indexes[j]))
+ break;
+ }
+ }
+ }
+
+ if (j == indexesLength)
+ {
+ if (returnType != null)
+ {
+ if (candidates[i].PropertyType.IsPrimitive)
+ {
+ if (!(returnType.UnderlyingSystemType.IsRuntimeImplemented()) ||
+ !CanChangePrimitive(returnType.UnderlyingSystemType, candidates[i].PropertyType.UnderlyingSystemType))
+ continue;
+ }
+ else
+ {
+ if (!candidates[i].PropertyType.IsAssignableFrom(returnType))
+ continue;
+ }
+ }
+ candidates[CurIdx++] = candidates[i];
+ }
+ }
+ if (CurIdx == 0)
+ return null;
+ if (CurIdx == 1)
+ return candidates[0];
+
+ // Walk all of the properties looking the most specific method to invoke
+ int currentMin = 0;
+ bool ambig = false;
+ int[] paramOrder = new int[indexesLength];
+ for (i = 0; i < indexesLength; i++)
+ paramOrder[i] = i;
+ for (i = 1; i < CurIdx; i++)
+ {
+ int newMin = FindMostSpecificType(candidates[currentMin].PropertyType, candidates[i].PropertyType, returnType);
+ if (newMin == 0 && indexes != null)
+ newMin = FindMostSpecific(candidates[currentMin].GetIndexParameters(),
+ paramOrder,
+ null,
+ candidates[i].GetIndexParameters(),
+ paramOrder,
+ null,
+ indexes,
+ null);
+ if (newMin == 0)
+ {
+ newMin = FindMostSpecificProperty(candidates[currentMin], candidates[i]);
+ if (newMin == 0)
+ ambig = true;
+ }
+ if (newMin == 2)
+ {
+ ambig = false;
+ currentMin = i;
+ }
+ }
+
+ if (ambig)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+ return candidates[currentMin];
+ }
+
+ // ChangeType
+ // The default binder doesn't support any change type functionality.
+ // This is because the default is built into the low level invoke code.
+ public override object ChangeType(object value, Type type, CultureInfo cultureInfo)
+ {
+ throw new NotSupportedException(SR.NotSupported_ChangeType);
+ }
+
+ public sealed override void ReorderArgumentArray(ref object[] args, object state)
+ {
+ BinderState binderState = (BinderState)state;
+ ReorderParams(binderState._argsMap, args);
+ if (binderState._isParamArray)
+ {
+ int paramArrayPos = args.Length - 1;
+ if (args.Length == binderState._originalSize)
+ args[paramArrayPos] = ((object[])args[paramArrayPos])[0];
+ else
+ {
+ // must be args.Length < state.originalSize
+ object[] newArgs = new object[args.Length];
+ Array.Copy(args, 0, newArgs, 0, paramArrayPos);
+ for (int i = paramArrayPos, j = 0; i < newArgs.Length; i++, j++)
+ {
+ newArgs[i] = ((object[])args[paramArrayPos])[j];
+ }
+ args = newArgs;
+ }
+ }
+ else
+ {
+ if (args.Length > binderState._originalSize)
+ {
+ object[] newArgs = new object[binderState._originalSize];
+ Array.Copy(args, 0, newArgs, 0, binderState._originalSize);
+ args = newArgs;
+ }
+ }
+ }
+
+ // Return any exact bindings that may exist. (This method is not defined on the
+ // Binder and is used by RuntimeType.)
+ public static MethodBase ExactBinding(MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (match == null)
+ throw new ArgumentNullException(nameof(match));
+
+ MethodBase[] aExactMatches = new MethodBase[match.Length];
+ int cExactMatches = 0;
+
+ for (int i = 0; i < match.Length; i++)
+ {
+ ParameterInfo[] par = match[i].GetParametersNoCopy();
+ if (par.Length == 0)
+ {
+ continue;
+ }
+ int j;
+ for (j = 0; j < types.Length; j++)
+ {
+ Type pCls = par[j].ParameterType;
+
+ // If the classes exactly match continue
+ if (!pCls.Equals(types[j]))
+ break;
+ }
+ if (j < types.Length)
+ continue;
+
+ // Add the exact match to the array of exact matches.
+ aExactMatches[cExactMatches] = match[i];
+ cExactMatches++;
+ }
+
+ if (cExactMatches == 0)
+ return null;
+
+ if (cExactMatches == 1)
+ return aExactMatches[0];
+
+ return FindMostDerivedNewSlotMeth(aExactMatches, cExactMatches);
+ }
+
+ // Return any exact bindings that may exist. (This method is not defined on the
+ // Binder and is used by RuntimeType.)
+ public static PropertyInfo ExactPropertyBinding(PropertyInfo[] match, Type returnType, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (match == null)
+ throw new ArgumentNullException(nameof(match));
+
+ PropertyInfo bestMatch = null;
+ int typesLength = (types != null) ? types.Length : 0;
+ for (int i = 0; i < match.Length; i++)
+ {
+ ParameterInfo[] par = match[i].GetIndexParameters();
+ int j;
+ for (j = 0; j < typesLength; j++)
+ {
+ Type pCls = par[j].ParameterType;
+
+ // If the classes exactly match continue
+ if (pCls != types[j])
+ break;
+ }
+ if (j < typesLength)
+ continue;
+ if (returnType != null && returnType != match[i].PropertyType)
+ continue;
+
+ if (bestMatch != null)
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+
+ bestMatch = match[i];
+ }
+ return bestMatch;
+ }
+
+ private static int FindMostSpecific(ParameterInfo[] p1, int[] paramOrder1, Type paramArrayType1,
+ ParameterInfo[] p2, int[] paramOrder2, Type paramArrayType2,
+ Type[] types, object[] args)
+ {
+ // A method using params is always less specific than one not using params
+ if (paramArrayType1 != null && paramArrayType2 == null) return 2;
+ if (paramArrayType2 != null && paramArrayType1 == null) return 1;
+
+ // now either p1 and p2 both use params or neither does.
+
+ bool p1Less = false;
+ bool p2Less = false;
+
+ for (int i = 0; i < types.Length; i++)
+ {
+ if (args != null && args[i] == Type.Missing)
+ continue;
+
+ Type c1, c2;
+
+ // If a param array is present, then either
+ // the user re-ordered the parameters in which case
+ // the argument to the param array is either an array
+ // in which case the params is conceptually ignored and so paramArrayType1 == null
+ // or the argument to the param array is a single element
+ // in which case paramOrder[i] == p1.Length - 1 for that element
+ // or the user did not re-order the parameters in which case
+ // the paramOrder array could contain indexes larger than p.Length - 1 (see VSW 577286)
+ // so any index >= p.Length - 1 is being put in the param array
+
+ if (paramArrayType1 != null && paramOrder1[i] >= p1.Length - 1)
+ c1 = paramArrayType1;
+ else
+ c1 = p1[paramOrder1[i]].ParameterType;
+
+ if (paramArrayType2 != null && paramOrder2[i] >= p2.Length - 1)
+ c2 = paramArrayType2;
+ else
+ c2 = p2[paramOrder2[i]].ParameterType;
+
+ if (c1 == c2) continue;
+
+ switch (FindMostSpecificType(c1, c2, types[i]))
+ {
+ case 0: return 0;
+ case 1: p1Less = true; break;
+ case 2: p2Less = true; break;
+ }
+ }
+
+ // Two way p1Less and p2Less can be equal. All the arguments are the
+ // same they both equal false, otherwise there were things that both
+ // were the most specific type on....
+ if (p1Less == p2Less)
+ {
+ // if we cannot tell which is a better match based on parameter types (p1Less == p2Less),
+ // let's see which one has the most matches without using the params array (the longer one wins).
+ if (!p1Less && args != null)
+ {
+ if (p1.Length > p2.Length)
+ {
+ return 1;
+ }
+ else if (p2.Length > p1.Length)
+ {
+ return 2;
+ }
+ }
+
+ return 0;
+ }
+ else
+ {
+ return (p1Less == true) ? 1 : 2;
+ }
+ }
+
+ private static int FindMostSpecificType(Type c1, Type c2, Type t)
+ {
+ // If the two types are exact move on...
+ if (c1 == c2)
+ return 0;
+
+ if (c1 == t)
+ return 1;
+
+ if (c2 == t)
+ return 2;
+
+ bool c1FromC2;
+ bool c2FromC1;
+
+ if (c1.IsByRef || c2.IsByRef)
+ {
+ if (c1.IsByRef && c2.IsByRef)
+ {
+ c1 = c1.GetElementType();
+ c2 = c2.GetElementType();
+ }
+ else if (c1.IsByRef)
+ {
+ if (c1.GetElementType() == c2)
+ return 2;
+
+ c1 = c1.GetElementType();
+ }
+ else
+ {
+ if (c2.GetElementType() == c1)
+ return 1;
+
+ c2 = c2.GetElementType();
+ }
+ }
+
+
+ if (c1.IsPrimitive && c2.IsPrimitive)
+ {
+ c1FromC2 = CanChangePrimitive(c2, c1);
+ c2FromC1 = CanChangePrimitive(c1, c2);
+ }
+ else
+ {
+ c1FromC2 = c1.IsAssignableFrom(c2);
+ c2FromC1 = c2.IsAssignableFrom(c1);
+ }
+
+ if (c1FromC2 == c2FromC1)
+ return 0;
+
+ if (c1FromC2)
+ {
+ return 2;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+ private static int FindMostSpecificMethod(MethodBase m1, int[] paramOrder1, Type paramArrayType1,
+ MethodBase m2, int[] paramOrder2, Type paramArrayType2,
+ Type[] types, object[] args)
+ {
+ // Find the most specific method based on the parameters.
+ int res = FindMostSpecific(m1.GetParametersNoCopy(), paramOrder1, paramArrayType1,
+ m2.GetParametersNoCopy(), paramOrder2, paramArrayType2, types, args);
+
+ // If the match was not ambigous then return the result.
+ if (res != 0)
+ return res;
+
+ // Check to see if the methods have the exact same name and signature.
+ if (CompareMethodSig(m1, m2))
+ {
+ // Determine the depth of the declaring types for both methods.
+ int hierarchyDepth1 = GetHierarchyDepth(m1.DeclaringType);
+ int hierarchyDepth2 = GetHierarchyDepth(m2.DeclaringType);
+
+ // The most derived method is the most specific one.
+ if (hierarchyDepth1 == hierarchyDepth2)
+ {
+ return 0;
+ }
+ else if (hierarchyDepth1 < hierarchyDepth2)
+ {
+ return 2;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+ // The match is ambigous.
+ return 0;
+ }
+
+ private static int FindMostSpecificField(FieldInfo cur1, FieldInfo cur2)
+ {
+ // Check to see if the fields have the same name.
+ if (cur1.Name == cur2.Name)
+ {
+ int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType);
+ int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType);
+
+ if (hierarchyDepth1 == hierarchyDepth2)
+ {
+ Debug.Assert(cur1.IsStatic != cur2.IsStatic, "hierarchyDepth1 == hierarchyDepth2");
+ return 0;
+ }
+ else if (hierarchyDepth1 < hierarchyDepth2)
+ return 2;
+ else
+ return 1;
+ }
+
+ // The match is ambigous.
+ return 0;
+ }
+
+ private static int FindMostSpecificProperty(PropertyInfo cur1, PropertyInfo cur2)
+ {
+ // Check to see if the fields have the same name.
+ if (cur1.Name == cur2.Name)
+ {
+ int hierarchyDepth1 = GetHierarchyDepth(cur1.DeclaringType);
+ int hierarchyDepth2 = GetHierarchyDepth(cur2.DeclaringType);
+
+ if (hierarchyDepth1 == hierarchyDepth2)
+ {
+ return 0;
+ }
+ else if (hierarchyDepth1 < hierarchyDepth2)
+ return 2;
+ else
+ return 1;
+ }
+
+ // The match is ambigous.
+ return 0;
+ }
+
+ public static bool CompareMethodSig(MethodBase m1, MethodBase m2)
+ {
+ ParameterInfo[] params1 = m1.GetParametersNoCopy();
+ ParameterInfo[] params2 = m2.GetParametersNoCopy();
+
+ if (params1.Length != params2.Length)
+ return false;
+
+ int numParams = params1.Length;
+ for (int i = 0; i < numParams; i++)
+ {
+ if (params1[i].ParameterType != params2[i].ParameterType)
+ return false;
+ }
+
+ return true;
+ }
+
+ private static int GetHierarchyDepth(Type t)
+ {
+ int depth = 0;
+
+ Type currentType = t;
+ do
+ {
+ depth++;
+ currentType = currentType.BaseType;
+ } while (currentType != null);
+
+ return depth;
+ }
+
+ internal static MethodBase FindMostDerivedNewSlotMeth(MethodBase[] match, int cMatches)
+ {
+ int deepestHierarchy = 0;
+ MethodBase methWithDeepestHierarchy = null;
+
+ for (int i = 0; i < cMatches; i++)
+ {
+ // Calculate the depth of the hierarchy of the declaring type of the
+ // current method.
+ int currentHierarchyDepth = GetHierarchyDepth(match[i].DeclaringType);
+
+ // The two methods have the same name, signature, and hierarchy depth.
+ // This can only happen if at least one is vararg or generic.
+ if (currentHierarchyDepth == deepestHierarchy)
+ {
+ throw new AmbiguousMatchException(SR.Arg_AmbiguousMatchException);
+ }
+
+ // Check to see if this method is on the most derived class.
+ if (currentHierarchyDepth > deepestHierarchy)
+ {
+ deepestHierarchy = currentHierarchyDepth;
+ methWithDeepestHierarchy = match[i];
+ }
+ }
+
+ return methWithDeepestHierarchy;
+ }
+
+ // This method will sort the vars array into the mapping order stored
+ // in the paramOrder array.
+ private static void ReorderParams(int[] paramOrder, object[] vars)
+ {
+ object[] varsCopy = new object[vars.Length];
+ for (int i = 0; i < vars.Length; i++)
+ varsCopy[i] = vars[i];
+
+ for (int i = 0; i < vars.Length; i++)
+ vars[i] = varsCopy[paramOrder[i]];
+ }
+
+ // This method will create the mapping between the Parameters and the underlying
+ // data based upon the names array. The names array is stored in the same order
+ // as the values and maps to the parameters of the method. We store the mapping
+ // from the parameters to the names in the paramOrder array. All parameters that
+ // don't have matching names are then stored in the array in order.
+ private static bool CreateParamOrder(int[] paramOrder, ParameterInfo[] pars, string[] names)
+ {
+ bool[] used = new bool[pars.Length];
+
+ // Mark which parameters have not been found in the names list
+ for (int i = 0; i < pars.Length; i++)
+ paramOrder[i] = -1;
+ // Find the parameters with names.
+ for (int i = 0; i < names.Length; i++)
+ {
+ int j;
+ for (j = 0; j < pars.Length; j++)
+ {
+ if (names[i].Equals(pars[j].Name))
+ {
+ paramOrder[j] = i;
+ used[i] = true;
+ break;
+ }
+ }
+ // This is an error condition. The name was not found. This
+ // method must not match what we sent.
+ if (j == pars.Length)
+ return false;
+ }
+
+ // Now we fill in the holes with the parameters that are unused.
+ int pos = 0;
+ for (int i = 0; i < pars.Length; i++)
+ {
+ if (paramOrder[i] == -1)
+ {
+ for (; pos < pars.Length; pos++)
+ {
+ if (!used[pos])
+ {
+ paramOrder[i] = pos;
+ pos++;
+ break;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ internal class BinderState
+ {
+ internal readonly int[] _argsMap;
+ internal readonly int _originalSize;
+ internal readonly bool _isParamArray;
+
+ internal BinderState(int[] argsMap, int originalSize, bool isParamArray)
+ {
+ _argsMap = argsMap;
+ _originalSize = originalSize;
+ _isParamArray = isParamArray;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs b/src/mscorlib/shared/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs
new file mode 100644
index 0000000000..893d7b8bc9
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/CodeAnalysis/SuppressMessageAttribute.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** An attribute to suppress violation messages/warnings
+** by static code analysis tools.
+**
+**
+===========================================================*/
+
+namespace System.Diagnostics.CodeAnalysis
+{
+ [AttributeUsage(
+ AttributeTargets.All,
+ Inherited = false,
+ AllowMultiple = true
+ )
+ ]
+ [Conditional("CODE_ANALYSIS")]
+ public sealed class SuppressMessageAttribute : Attribute
+ {
+ public SuppressMessageAttribute(string category, string checkId)
+ {
+ Category = category;
+ CheckId = checkId;
+ }
+
+ public string Category { get; }
+ public string CheckId { get; }
+ public string Scope { get; set; }
+ public string Target { get; set; }
+ public string MessageId { get; set; }
+ public string Justification { get; set; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/ConditionalAttribute.cs b/src/mscorlib/shared/System/Diagnostics/ConditionalAttribute.cs
new file mode 100644
index 0000000000..d5bca6e208
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/ConditionalAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Diagnostics
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)]
+ public sealed class ConditionalAttribute : Attribute
+ {
+ public ConditionalAttribute(string conditionString)
+ {
+ ConditionString = conditionString;
+ }
+
+ public string ConditionString { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Debug.cs b/src/mscorlib/shared/System/Diagnostics/Debug.cs
new file mode 100644
index 0000000000..59f3c378da
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Debug.cs
@@ -0,0 +1,323 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// Do not remove this, it is needed to retain calls to these conditional methods in release builds
+#define DEBUG
+
+namespace System.Diagnostics
+{
+ /// <summary>
+ /// Provides a set of properties and methods for debugging code.
+ /// </summary>
+ public static partial class Debug
+ {
+ private static readonly object s_lock = new object();
+
+ public static bool AutoFlush { get { return true; } set { } }
+
+ [ThreadStatic]
+ private static int s_indentLevel;
+ public static int IndentLevel
+ {
+ get
+ {
+ return s_indentLevel;
+ }
+ set
+ {
+ s_indentLevel = value < 0 ? 0 : value;
+ }
+ }
+
+ private static int s_indentSize = 4;
+ public static int IndentSize
+ {
+ get
+ {
+ return s_indentSize;
+ }
+ set
+ {
+ s_indentSize = value < 0 ? 0 : value;
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Close() { }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Flush() { }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Indent()
+ {
+ IndentLevel++;
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Unindent()
+ {
+ IndentLevel--;
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Print(string message)
+ {
+ Write(message);
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Print(string format, params object[] args)
+ {
+ Write(string.Format(null, format, args));
+ }
+
+ private static readonly object s_ForLock = new Object();
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Assert(bool condition)
+ {
+ Assert(condition, string.Empty, string.Empty);
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Assert(bool condition, string message)
+ {
+ Assert(condition, message, string.Empty);
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Assert(bool condition, string message, string detailMessage)
+ {
+ if (!condition)
+ {
+ string stackTrace;
+
+ try
+ {
+ stackTrace = Internal.Runtime.Augments.EnvironmentAugments.StackTrace;
+ }
+ catch
+ {
+ stackTrace = "";
+ }
+
+ WriteLine(FormatAssert(stackTrace, message, detailMessage));
+ s_ShowAssertDialog(stackTrace, message, detailMessage);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Fail(string message)
+ {
+ Assert(false, message, string.Empty);
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Fail(string message, string detailMessage)
+ {
+ Assert(false, message, detailMessage);
+ }
+
+ private static string FormatAssert(string stackTrace, string message, string detailMessage)
+ {
+ string newLine = GetIndentString() + Environment.NewLine;
+ return SR.DebugAssertBanner + newLine
+ + SR.DebugAssertShortMessage + newLine
+ + message + newLine
+ + SR.DebugAssertLongMessage + newLine
+ + detailMessage + newLine
+ + stackTrace;
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Assert(bool condition, string message, string detailMessageFormat, params object[] args)
+ {
+ Assert(condition, message, string.Format(detailMessageFormat, args));
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteLine(string message)
+ {
+ Write(message + Environment.NewLine);
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Write(string message)
+ {
+ lock (s_lock)
+ {
+ if (message == null)
+ {
+ s_WriteCore(string.Empty);
+ return;
+ }
+ if (s_needIndent)
+ {
+ message = GetIndentString() + message;
+ s_needIndent = false;
+ }
+ s_WriteCore(message);
+ if (message.EndsWith(Environment.NewLine))
+ {
+ s_needIndent = true;
+ }
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteLine(object value)
+ {
+ WriteLine(value?.ToString());
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteLine(object value, string category)
+ {
+ WriteLine(value?.ToString(), category);
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteLine(string format, params object[] args)
+ {
+ WriteLine(string.Format(null, format, args));
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteLine(string message, string category)
+ {
+ if (category == null)
+ {
+ WriteLine(message);
+ }
+ else
+ {
+ WriteLine(category + ":" + message);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Write(object value)
+ {
+ Write(value?.ToString());
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Write(string message, string category)
+ {
+ if (category == null)
+ {
+ Write(message);
+ }
+ else
+ {
+ Write(category + ":" + message);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void Write(object value, string category)
+ {
+ Write(value?.ToString(), category);
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteIf(bool condition, string message)
+ {
+ if (condition)
+ {
+ Write(message);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteIf(bool condition, object value)
+ {
+ if (condition)
+ {
+ Write(value);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteIf(bool condition, string message, string category)
+ {
+ if (condition)
+ {
+ Write(message, category);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteIf(bool condition, object value, string category)
+ {
+ if (condition)
+ {
+ Write(value, category);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteLineIf(bool condition, object value)
+ {
+ if (condition)
+ {
+ WriteLine(value);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteLineIf(bool condition, object value, string category)
+ {
+ if (condition)
+ {
+ WriteLine(value, category);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteLineIf(bool condition, string message)
+ {
+ if (condition)
+ {
+ WriteLine(message);
+ }
+ }
+
+ [System.Diagnostics.Conditional("DEBUG")]
+ public static void WriteLineIf(bool condition, string message, string category)
+ {
+ if (condition)
+ {
+ WriteLine(message, category);
+ }
+ }
+
+ private static bool s_needIndent;
+
+ private static string s_indentString;
+
+ private static string GetIndentString()
+ {
+ int indentCount = IndentSize * IndentLevel;
+ if (s_indentString?.Length == indentCount)
+ {
+ return s_indentString;
+ }
+ return s_indentString = new string(' ', indentCount);
+ }
+
+ private sealed class DebugAssertException : Exception
+ {
+ internal DebugAssertException(string message, string detailMessage, string stackTrace) :
+ base(message + Environment.NewLine + detailMessage + Environment.NewLine + stackTrace)
+ {
+ }
+ }
+
+ // internal and not readonly so that the tests can swap this out.
+ internal static Action<string, string, string> s_ShowAssertDialog = ShowAssertDialog;
+ internal static Action<string> s_WriteCore = WriteCore;
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/ActivityTracker.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/ActivityTracker.cs
new file mode 100644
index 0000000000..e32abd2895
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/ActivityTracker.cs
@@ -0,0 +1,665 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Threading;
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+using System.Threading.Tasks;
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Tracks activities. This is meant to be a singleton (accessed by the ActivityTracer.Instance static property)
+ ///
+ /// Logically this is simply holds the m_current variable that holds the async local that holds the current ActivityInfo
+ /// An ActivityInfo is represents a activity (which knows its creator and thus knows its path).
+ ///
+ /// Most of the magic is in the async local (it gets copied to new tasks)
+ ///
+ /// On every start event call OnStart
+ ///
+ /// Guid activityID;
+ /// Guid relatedActivityID;
+ /// if (OnStart(activityName, out activityID, out relatedActivityID, ForceStop, options))
+ /// // Log Start event with activityID and relatedActivityID
+ ///
+ /// On every stop event call OnStop
+ ///
+ /// Guid activityID;
+ /// if (OnStop(activityName, ref activityID ForceStop))
+ /// // Stop event with activityID
+ ///
+ /// On any normal event log the event with activityTracker.CurrentActivityId
+ /// </summary>
+ internal class ActivityTracker
+ {
+
+ /// <summary>
+ /// Called on work item begins. The activity name = providerName + activityName without 'Start' suffix.
+ /// It updates CurrentActivityId to track.
+ ///
+ /// It returns true if the Start should be logged, otherwise (if it is illegal recursion) it return false.
+ ///
+ /// The start event should use as its activity ID the CurrentActivityId AFTER calling this routine and its
+ /// RelatedActivityID the CurrentActivityId BEFORE calling this routine (the creator).
+ ///
+ /// If activity tracing is not on, then activityId and relatedActivityId are not set
+ /// </summary>
+ public void OnStart(string providerName, string activityName, int task, ref Guid activityId, ref Guid relatedActivityId, EventActivityOptions options)
+ {
+ if (m_current == null) // We are not enabled
+ {
+ // We used to rely on the TPL provider turning us on, but that has the disadvantage that you don't get Start-Stop tracking
+ // until you use Tasks for the first time (which you may never do). Thus we change it to pull rather tan push for whether
+ // we are enabled.
+ if (m_checkedForEnable)
+ return;
+ m_checkedForEnable = true;
+ if (TplEtwProvider.Log.IsEnabled(EventLevel.Informational, TplEtwProvider.Keywords.TasksFlowActivityIds))
+ Enable();
+ if (m_current == null)
+ return;
+ }
+
+
+ Debug.Assert((options & EventActivityOptions.Disable) == 0);
+
+ var currentActivity = m_current.Value;
+ var fullActivityName = NormalizeActivityName(providerName, activityName, task);
+
+ var etwLog = TplEtwProvider.Log;
+ if (etwLog.Debug)
+ {
+ etwLog.DebugFacilityMessage("OnStartEnter", fullActivityName);
+ etwLog.DebugFacilityMessage("OnStartEnterActivityState", ActivityInfo.LiveActivities(currentActivity));
+ }
+
+ if (currentActivity != null)
+ {
+ // Stop activity tracking if we reached the maximum allowed depth
+ if (currentActivity.m_level >= MAX_ACTIVITY_DEPTH)
+ {
+ activityId = Guid.Empty;
+ relatedActivityId = Guid.Empty;
+ if (etwLog.Debug)
+ etwLog.DebugFacilityMessage("OnStartRET", "Fail");
+ return;
+ }
+ // Check for recursion, and force-stop any activities if the activity already started.
+ if ((options & EventActivityOptions.Recursive) == 0)
+ {
+ ActivityInfo existingActivity = FindActiveActivity(fullActivityName, currentActivity);
+ if (existingActivity != null)
+ {
+ OnStop(providerName, activityName, task, ref activityId);
+ currentActivity = m_current.Value;
+ }
+ }
+ }
+
+ // Get a unique ID for this activity.
+ long id;
+ if (currentActivity == null)
+ id = Interlocked.Increment(ref m_nextId);
+ else
+ id = Interlocked.Increment(ref currentActivity.m_lastChildID);
+
+ // The previous ID is my 'causer' and becomes my related activity ID
+ relatedActivityId = EventSource.CurrentThreadActivityId;
+
+ // Add to the list of started but not stopped activities.
+ ActivityInfo newActivity = new ActivityInfo(fullActivityName, id, currentActivity, relatedActivityId, options);
+ m_current.Value = newActivity;
+
+ // Remember the current ID so we can log it
+ activityId = newActivity.ActivityId;
+
+ if (etwLog.Debug)
+ {
+ etwLog.DebugFacilityMessage("OnStartRetActivityState", ActivityInfo.LiveActivities(newActivity));
+ etwLog.DebugFacilityMessage1("OnStartRet", activityId.ToString(), relatedActivityId.ToString());
+ }
+ }
+
+ /// <summary>
+ /// Called when a work item stops. The activity name = providerName + activityName without 'Stop' suffix.
+ /// It updates m_current variable to track this fact. The Stop event associated with stop should log the ActivityID associated with the event.
+ ///
+ /// If activity tracing is not on, then activityId and relatedActivityId are not set
+ /// </summary>
+ public void OnStop(string providerName, string activityName, int task, ref Guid activityId)
+ {
+ if (m_current == null) // We are not enabled
+ return;
+
+ var fullActivityName = NormalizeActivityName(providerName, activityName, task);
+
+ var etwLog = TplEtwProvider.Log;
+ if (etwLog.Debug)
+ {
+ etwLog.DebugFacilityMessage("OnStopEnter", fullActivityName);
+ etwLog.DebugFacilityMessage("OnStopEnterActivityState", ActivityInfo.LiveActivities(m_current.Value));
+ }
+
+ for (; ; ) // This is a retry loop.
+ {
+ ActivityInfo currentActivity = m_current.Value;
+ ActivityInfo newCurrentActivity = null; // if we have seen any live activities (orphans), at he first one we have seen.
+
+ // Search to find the activity to stop in one pass. This insures that we don't let one mistake
+ // (stopping something that was not started) cause all active starts to be stopped
+ // By first finding the target start to stop we are more robust.
+ ActivityInfo activityToStop = FindActiveActivity(fullActivityName, currentActivity);
+
+ // ignore stops where we can't find a start because we may have popped them previously.
+ if (activityToStop == null)
+ {
+ activityId = Guid.Empty;
+ // TODO add some logging about this. Basically could not find matching start.
+ if (etwLog.Debug)
+ etwLog.DebugFacilityMessage("OnStopRET", "Fail");
+ return;
+ }
+
+ activityId = activityToStop.ActivityId;
+
+ // See if there are any orphans that need to be stopped.
+ ActivityInfo orphan = currentActivity;
+ while (orphan != activityToStop && orphan != null)
+ {
+ if (orphan.m_stopped != 0) // Skip dead activities.
+ {
+ orphan = orphan.m_creator;
+ continue;
+ }
+ if (orphan.CanBeOrphan())
+ {
+ // We can't pop anything after we see a valid orphan, remember this for later when we update m_current.
+ if (newCurrentActivity == null)
+ newCurrentActivity = orphan;
+ }
+ else
+ {
+ orphan.m_stopped = 1;
+ Debug.Assert(orphan.m_stopped != 0);
+ }
+ orphan = orphan.m_creator;
+ }
+
+ // try to Stop the activity atomically. Other threads may be trying to do this as well.
+ if (Interlocked.CompareExchange(ref activityToStop.m_stopped, 1, 0) == 0)
+ {
+ // I succeeded stopping this activity. Now we update our m_current pointer
+
+ // If I haven't yet determined the new current activity, it is my creator.
+ if (newCurrentActivity == null)
+ newCurrentActivity = activityToStop.m_creator;
+
+ m_current.Value = newCurrentActivity;
+
+ if (etwLog.Debug)
+ {
+ etwLog.DebugFacilityMessage("OnStopRetActivityState", ActivityInfo.LiveActivities(newCurrentActivity));
+ etwLog.DebugFacilityMessage("OnStopRet", activityId.ToString());
+ }
+ return;
+ }
+ // We failed to stop it. We must have hit a race to stop it. Just start over and try again.
+ }
+ }
+
+ /// <summary>
+ /// Turns on activity tracking. It is sticky, once on it stays on (race issues otherwise)
+ /// </summary>
+ public void Enable()
+ {
+ if (m_current == null)
+ {
+ // Catch the not Implemented
+ try
+ {
+ m_current = new AsyncLocal<ActivityInfo>(ActivityChanging);
+ }
+ catch (NotImplementedException) {
+#if (!ES_BUILD_PCL && ! ES_BUILD_PN)
+ // send message to debugger without delay
+ System.Diagnostics.Debugger.Log(0, null, "Activity Enabled() called but AsyncLocals Not Supported (pre V4.6). Ignoring Enable");
+#endif
+ }
+ }
+ }
+
+ /// <summary>
+ /// An activity tracker is a singleton, this is how you get the one and only instance.
+ /// </summary>
+ public static ActivityTracker Instance { get { return s_activityTrackerInstance; } }
+
+
+ #region private
+
+ /// <summary>
+ /// The current activity ID. Use this to log normal events.
+ /// </summary>
+ private Guid CurrentActivityId { get { return m_current.Value.ActivityId; } }
+
+ /// <summary>
+ /// Searched for a active (nonstopped) activity with the given name. Returns null if not found.
+ /// </summary>
+ private ActivityInfo FindActiveActivity(string name, ActivityInfo startLocation)
+ {
+ var activity = startLocation;
+ while (activity != null)
+ {
+ if (name == activity.m_name && activity.m_stopped == 0)
+ return activity;
+ activity = activity.m_creator;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Strip out "Start" or "End" suffix from activity name and add providerName prefix.
+ /// If 'task' it does not end in Start or Stop and Task is non-zero use that as the name of the activity
+ /// </summary>
+ private string NormalizeActivityName(string providerName, string activityName, int task)
+ {
+ if (activityName.EndsWith(EventSource.s_ActivityStartSuffix, StringComparison.Ordinal))
+ activityName = activityName.Substring(0, activityName.Length - EventSource.s_ActivityStartSuffix.Length);
+ else if (activityName.EndsWith(EventSource.s_ActivityStopSuffix, StringComparison.Ordinal))
+ activityName = activityName.Substring(0, activityName.Length - EventSource.s_ActivityStopSuffix.Length);
+ else if (task != 0)
+ activityName = "task" + task.ToString();
+
+ // We use provider name to distinguish between activities from different providers.
+ return providerName + activityName;
+ }
+
+ // *******************************************************************************
+ /// <summary>
+ /// An ActivityInfo represents a particular activity. It is almost read-only. The only
+ /// fields that change after creation are
+ /// m_lastChildID - used to generate unique IDs for the children activities and for the most part can be ignored.
+ /// m_stopped - indicates that this activity is dead
+ /// This read-only-ness is important because an activity's m_creator chain forms the
+ /// 'Path of creation' for the activity (which is also its unique ID) but is also used as
+ /// the 'list of live parents' which indicate of those ancestors, which are alive (if they
+ /// are not marked dead they are alive).
+ /// </summary>
+ private class ActivityInfo
+ {
+ public ActivityInfo(string name, long uniqueId, ActivityInfo creator, Guid activityIDToRestore, EventActivityOptions options)
+ {
+ m_name = name;
+ m_eventOptions = options;
+ m_creator = creator;
+ m_uniqueId = uniqueId;
+ m_level = creator != null ? creator.m_level + 1 : 0;
+ m_activityIdToRestore = activityIDToRestore;
+
+ // Create a nice GUID that encodes the chain of activities that started this one.
+ CreateActivityPathGuid(out m_guid, out m_activityPathGuidOffset);
+ }
+
+ public Guid ActivityId
+ {
+ get
+ {
+ return m_guid;
+ }
+ }
+
+ public static string Path(ActivityInfo activityInfo)
+ {
+ if (activityInfo == null)
+ return ("");
+ return Path(activityInfo.m_creator) + "/" + activityInfo.m_uniqueId.ToString();
+ }
+
+ public override string ToString()
+ {
+ return m_name + "(" + Path(this) + (m_stopped != 0 ? ",DEAD)" : ")");
+ }
+
+ public static string LiveActivities(ActivityInfo list)
+ {
+ if (list == null)
+ return "";
+ return list.ToString() + ";" + LiveActivities(list.m_creator);
+ }
+
+ public bool CanBeOrphan()
+ {
+ if ((m_eventOptions & EventActivityOptions.Detachable) != 0)
+ return true;
+ return false;
+ }
+
+ #region private
+
+ #region CreateActivityPathGuid
+ /// <summary>
+ /// Logically every activity Path (see Path()) that describes the activities that caused this
+ /// (rooted in an activity that predates activity tracking.
+ ///
+ /// We wish to encode this path in the Guid to the extent that we can. Many of the paths have
+ /// many small numbers in them and we take advantage of this in the encoding to output as long
+ /// a path in the GUID as possible.
+ ///
+ /// Because of the possibility of GUID collision, we only use 96 of the 128 bits of the GUID
+ /// for encoding the path. The last 32 bits are a simple checksum (and random number) that
+ /// identifies this as using the convention defined here.
+ ///
+ /// It returns both the GUID which has the path as well as the offset that points just beyond
+ /// the end of the activity (so it can be appended to). Note that if the end is in a nibble
+ /// (it uses nibbles instead of bytes as the unit of encoding, then it will point at the unfinished
+ /// byte (since the top nibble can't be zero you can determine if this is true by seeing if
+ /// this byte is nonZero. This offset is needed to efficiently create the ID for child activities.
+ /// </summary>
+ private unsafe void CreateActivityPathGuid(out Guid idRet, out int activityPathGuidOffset)
+ {
+ fixed (Guid* outPtr = &idRet)
+ {
+ int activityPathGuidOffsetStart = 0;
+ if (m_creator != null)
+ {
+ activityPathGuidOffsetStart = m_creator.m_activityPathGuidOffset;
+ idRet = m_creator.m_guid;
+ }
+ else
+ {
+ // TODO FIXME - differentiate between AD inside PCL
+ int appDomainID = 0;
+#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
+ appDomainID = System.Threading.Thread.GetDomainID();
+#endif
+ // We start with the appdomain number to make this unique among appdomains.
+ activityPathGuidOffsetStart = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)appDomainID);
+ }
+
+ activityPathGuidOffset = AddIdToGuid(outPtr, activityPathGuidOffsetStart, (uint)m_uniqueId);
+
+
+ // If the path does not fit, Make a GUID by incrementing rather than as a path, keeping as much of the path as possible
+ if (12 < activityPathGuidOffset)
+ CreateOverflowGuid(outPtr);
+ }
+ }
+
+ /// <summary>
+ /// If we can't fit the activity Path into the GUID we come here. What we do is simply
+ /// generate a 4 byte number (s_nextOverflowId). Then look for an ancestor that has
+ /// sufficient space for this ID. By doing this, we preserve the fact that this activity
+ /// is a child (of unknown depth) from that ancestor.
+ /// </summary>
+ private unsafe void CreateOverflowGuid(Guid* outPtr)
+ {
+ // Search backwards for an ancestor that has sufficient space to put the ID.
+ for (ActivityInfo ancestor = m_creator; ancestor != null; ancestor = ancestor.m_creator)
+ {
+ if (ancestor.m_activityPathGuidOffset <= 10) // we need at least 2 bytes.
+ {
+ uint id = unchecked((uint)Interlocked.Increment(ref ancestor.m_lastChildID)); // Get a unique ID
+ // Try to put the ID into the GUID
+ *outPtr = ancestor.m_guid;
+ int endId = AddIdToGuid(outPtr, ancestor.m_activityPathGuidOffset, id, true);
+
+ // Does it fit?
+ if (endId <= 12)
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// The encoding for a list of numbers used to make Activity GUIDs. Basically
+ /// we operate on nibbles (which are nice because they show up as hex digits). The
+ /// list is ended with a end nibble (0) and depending on the nibble value (Below)
+ /// the value is either encoded into nibble itself or it can spill over into the
+ /// bytes that follow.
+ /// </summary>
+ enum NumberListCodes : byte
+ {
+ End = 0x0, // ends the list. No valid value has this prefix.
+ LastImmediateValue = 0xA,
+
+ PrefixCode = 0xB, // all the 'long' encodings go here. If the next nibble is MultiByte1-4
+ // than this is a 'overflow' id. Unlike the hierarchical IDs these are
+ // allocated densely but don't tell you anything about nesting. we use
+ // these when we run out of space in the GUID to store the path.
+
+ MultiByte1 = 0xC, // 1 byte follows. If this Nibble is in the high bits, it the high bits of the number are stored in the low nibble.
+ // commented out because the code does not explicitly reference the names (but they are logically defined).
+ // MultiByte2 = 0xD, // 2 bytes follow (we don't bother with the nibble optimization)
+ // MultiByte3 = 0xE, // 3 bytes follow (we don't bother with the nibble optimization)
+ // MultiByte4 = 0xF, // 4 bytes follow (we don't bother with the nibble optimization)
+ }
+
+ /// Add the activity id 'id' to the output Guid 'outPtr' starting at the offset 'whereToAddId'
+ /// Thus if this number is 6 that is where 'id' will be added. This will return 13 (12
+ /// is the maximum number of bytes that fit in a GUID) if the path did not fit.
+ /// If 'overflow' is true, then the number is encoded as an 'overflow number (which has a
+ /// special (longer prefix) that indicates that this ID is allocated differently
+ private static unsafe int AddIdToGuid(Guid* outPtr, int whereToAddId, uint id, bool overflow = false)
+ {
+ byte* ptr = (byte*)outPtr;
+ byte* endPtr = ptr + 12;
+ ptr += whereToAddId;
+ if (endPtr <= ptr)
+ return 13; // 12 means we might exactly fit, 13 means we definately did not fit
+
+ if (0 < id && id <= (uint)NumberListCodes.LastImmediateValue && !overflow)
+ WriteNibble(ref ptr, endPtr, id);
+ else
+ {
+ uint len = 4;
+ if (id <= 0xFF)
+ len = 1;
+ else if (id <= 0xFFFF)
+ len = 2;
+ else if (id <= 0xFFFFFF)
+ len = 3;
+
+ if (overflow)
+ {
+ if (endPtr <= ptr + 2) // I need at least 2 bytes
+ return 13;
+
+ // Write out the prefix code nibble and the length nibble
+ WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.PrefixCode);
+ }
+ // The rest is the same for overflow and non-overflow case
+ WriteNibble(ref ptr, endPtr, (uint)NumberListCodes.MultiByte1 + (len - 1));
+
+ // Do we have an odd nibble? If so flush it or use it for the 12 byte case.
+ if (ptr < endPtr && *ptr != 0)
+ {
+ // If the value < 4096 we can use the nibble we are otherwise just outputting as padding.
+ if (id < 4096)
+ {
+ // Indicate this is a 1 byte multicode with 4 high order bits in the lower nibble.
+ *ptr = (byte)(((uint)NumberListCodes.MultiByte1 << 4) + (id >> 8));
+ id &= 0xFF; // Now we only want the low order bits.
+ }
+ ptr++;
+ }
+
+ // Write out the bytes.
+ while (0 < len)
+ {
+ if (endPtr <= ptr)
+ {
+ ptr++; // Indicate that we have overflowed
+ break;
+ }
+ *ptr++ = (byte)id;
+ id = (id >> 8);
+ --len;
+ }
+ }
+
+ // Compute the checksum
+ uint* sumPtr = (uint*)outPtr;
+ // We set the last DWORD the sum of the first 3 DWORDS in the GUID. This
+ sumPtr[3] = sumPtr[0] + sumPtr[1] + sumPtr[2] + 0x599D99AD; // This last number is a random number (it identifies us as us)
+
+ return (int)(ptr - ((byte*)outPtr));
+ }
+
+ /// <summary>
+ /// Write a single Nible 'value' (must be 0-15) to the byte buffer represented by *ptr.
+ /// Will not go past 'endPtr'. Also it assumes that we never write 0 so we can detect
+ /// whether a nibble has already been written to ptr because it will be nonzero.
+ /// Thus if it is non-zero it adds to the current byte, otherwise it advances and writes
+ /// the new byte (in the high bits) of the next byte.
+ /// </summary>
+ private static unsafe void WriteNibble(ref byte* ptr, byte* endPtr, uint value)
+ {
+ Debug.Assert(0 <= value && value < 16);
+ Debug.Assert(ptr < endPtr);
+
+ if (*ptr != 0)
+ *ptr++ |= (byte)value;
+ else
+ *ptr = (byte)(value << 4);
+ }
+
+ #endregion // CreateGuidForActivityPath
+
+ readonly internal string m_name; // The name used in the 'start' and 'stop' APIs to help match up
+ readonly long m_uniqueId; // a small number that makes this activity unique among its siblings
+ internal readonly Guid m_guid; // Activity Guid, it is basically an encoding of the Path() (see CreateActivityPathGuid)
+ internal readonly int m_activityPathGuidOffset; // Keeps track of where in m_guid the causality path stops (used to generated child GUIDs)
+ internal readonly int m_level; // current depth of the Path() of the activity (used to keep recursion under control)
+ readonly internal EventActivityOptions m_eventOptions; // Options passed to start.
+ internal long m_lastChildID; // used to create a unique ID for my children activities
+ internal int m_stopped; // This work item has stopped
+ readonly internal ActivityInfo m_creator; // My parent (creator). Forms the Path() for the activity.
+ readonly internal Guid m_activityIdToRestore; // The Guid to restore after a stop.
+ #endregion
+ }
+
+ // This callback is used to initialize the m_current AsyncLocal Variable.
+ // Its job is to keep the ETW Activity ID (part of thread local storage) in sync
+ // with m_current.ActivityID
+ void ActivityChanging(AsyncLocalValueChangedArgs<ActivityInfo> args)
+ {
+ ActivityInfo cur = args.CurrentValue;
+ ActivityInfo prev = args.PreviousValue;
+
+ // Are we popping off a value? (we have a prev, and it creator is cur)
+ // Then check if we should use the GUID at the time of the start event
+ if (prev != null && prev.m_creator == cur)
+ {
+ // If the saved activity ID is not the same as the creator activity
+ // that takes precedence (it means someone explicitly did a SetActivityID)
+ // Set it to that and get out
+ if (cur == null || prev.m_activityIdToRestore != cur.ActivityId)
+ {
+ EventSource.SetCurrentThreadActivityId(prev.m_activityIdToRestore);
+ return;
+ }
+ }
+
+ // OK we did not have an explicit SetActivityID set. Then we should be
+ // setting the activity to current ActivityInfo. However that activity
+ // might be dead, in which case we should skip it, so we never set
+ // the ID to dead things.
+ while (cur != null)
+ {
+ // We found a live activity (typically the first time), set it to that.
+ if (cur.m_stopped == 0)
+ {
+ EventSource.SetCurrentThreadActivityId(cur.ActivityId);
+ return;
+ }
+ cur = cur.m_creator;
+ }
+ // we can get here if there is no information on our activity stack (everything is dead)
+ // currently we do nothing, as that seems better than setting to Guid.Emtpy.
+ }
+
+ /// <summary>
+ /// Async local variables have the property that the are automatically copied whenever a task is created and used
+ /// while that task is running. Thus m_current 'flows' to any task that is caused by the current thread that
+ /// last set it.
+ ///
+ /// This variable points a a linked list that represents all Activities that have started but have not stopped.
+ /// </summary>
+ AsyncLocal<ActivityInfo> m_current;
+ bool m_checkedForEnable;
+
+ // Singleton
+ private static ActivityTracker s_activityTrackerInstance = new ActivityTracker();
+
+ // Used to create unique IDs at the top level. Not used for nested Ids (each activity has its own id generator)
+ static long m_nextId = 0;
+ private const ushort MAX_ACTIVITY_DEPTH = 100; // Limit maximum depth of activities to be tracked at 100.
+ // This will avoid leaking memory in case of activities that are never stopped.
+
+ #endregion
+ }
+
+#if ES_BUILD_STANDALONE || ES_BUILD_PN
+ /******************************** SUPPORT *****************************/
+ /// <summary>
+ /// This is supplied by the framework. It is has the semantics that the value is copied to any new Tasks that is created
+ /// by the current task. Thus all causally related code gets this value. Note that reads and writes to this VARIABLE
+ /// (not what it points it) to this does not need to be protected by locks because it is inherently thread local (you always
+ /// only get your thread local copy which means that you never have races.
+ /// </summary>
+ ///
+#if ES_BUILD_STANDALONE
+ [EventSource(Name = "Microsoft.Tasks.Nuget")]
+#else
+ [EventSource(Name = "System.Diagnostics.Tracing.TplEtwProvider")]
+#endif
+ internal class TplEtwProvider : EventSource
+ {
+ public class Keywords
+ {
+ public const EventKeywords TasksFlowActivityIds = (EventKeywords)0x80;
+ public const EventKeywords Debug = (EventKeywords)0x20000;
+ }
+
+ public static TplEtwProvider Log = new TplEtwProvider();
+ public bool Debug { get { return IsEnabled(EventLevel.Verbose, Keywords.Debug); } }
+
+ public void DebugFacilityMessage(string Facility, string Message) { WriteEvent(1, Facility, Message); }
+ public void DebugFacilityMessage1(string Facility, string Message, string Arg) { WriteEvent(2, Facility, Message, Arg); }
+ public void SetActivityId(Guid Id) { WriteEvent(3, Id); }
+ }
+#endif
+
+#if ES_BUILD_AGAINST_DOTNET_V35 || ES_BUILD_PCL || NO_ASYNC_LOCAL
+ // In these cases we don't have any Async local support. Do nothing.
+ internal sealed class AsyncLocalValueChangedArgs<T>
+ {
+ public T PreviousValue { get { return default(T); } }
+ public T CurrentValue { get { return default(T); } }
+
+ }
+
+ internal sealed class AsyncLocal<T>
+ {
+ public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>> valueChangedHandler) {
+ throw new NotImplementedException("AsyncLocal only available on V4.6 and above");
+ }
+ public T Value
+ {
+ get { return default(T); }
+ set { }
+ }
+ }
+#endif
+
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventActivityOptions.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventActivityOptions.cs
new file mode 100644
index 0000000000..782afbf869
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventActivityOptions.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// EventActivityOptions flags allow to specify different activity related characteristics.
+ /// </summary>
+ [Flags]
+ public enum EventActivityOptions
+ {
+ /// <summary>
+ /// No special options are added to the event.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// Disable Implicit Activity Tracking
+ /// </summary>
+ Disable = 0x2,
+
+ /// <summary>
+ /// Allow activity event to call itself (directly or indirectly)
+ /// </summary>
+ Recursive = 0x4,
+
+ /// <summary>
+ /// Allows event activity to live beyond its parent.
+ /// </summary>
+ Detachable = 0x8
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs
new file mode 100644
index 0000000000..b1f946464e
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventCounter.cs
@@ -0,0 +1,436 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+using System.Collections.Generic;
+using System.Threading;
+#if ES_BUILD_PCL
+ using System.Threading.Tasks;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Provides the ability to collect statistics through EventSource
+ /// </summary>
+ public class EventCounter
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EventCounter"/> class.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="eventSource">The event source.</param>
+ public EventCounter(string name, EventSource eventSource)
+ {
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ if (eventSource == null)
+ {
+ throw new ArgumentNullException(nameof(eventSource));
+ }
+
+ InitializeBuffer();
+ _name = name;
+ EventCounterGroup.AddEventCounter(eventSource, this);
+ }
+
+ /// <summary>
+ /// Writes the metric.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ public void WriteMetric(float value)
+ {
+ Enqueue(value);
+ }
+
+ #region private implementation
+
+ private readonly string _name;
+
+ #region Buffer Management
+
+ // Values buffering
+ private const int BufferedSize = 10;
+ private const float UnusedBufferSlotValue = float.NegativeInfinity;
+ private const int UnsetIndex = -1;
+ private volatile float[] _bufferedValues;
+ private volatile int _bufferedValuesIndex;
+
+ private void InitializeBuffer()
+ {
+ _bufferedValues = new float[BufferedSize];
+ for (int i = 0; i < _bufferedValues.Length; i++)
+ {
+ _bufferedValues[i] = UnusedBufferSlotValue;
+ }
+ }
+
+ private void Enqueue(float value)
+ {
+ // It is possible that two threads read the same bufferedValuesIndex, but only one will be able to write the slot, so that is okay.
+ int i = _bufferedValuesIndex;
+ while (true)
+ {
+ float result = Interlocked.CompareExchange(ref _bufferedValues[i], value, UnusedBufferSlotValue);
+ i++;
+ if (_bufferedValues.Length <= i)
+ {
+ // It is possible that two threads both think the buffer is full, but only one get to actually flush it, the other
+ // will eventually enter this code path and potentially calling Flushing on a buffer that is not full, and that's okay too.
+ lock (_bufferedValues)
+ {
+ Flush();
+ }
+ i = 0;
+ }
+
+ if (result == UnusedBufferSlotValue)
+ {
+ // CompareExchange succeeded
+ _bufferedValuesIndex = i;
+ return;
+ }
+ }
+ }
+
+ private void Flush()
+ {
+ for (int i = 0; i < _bufferedValues.Length; i++)
+ {
+ var value = Interlocked.Exchange(ref _bufferedValues[i], UnusedBufferSlotValue);
+ if (value != UnusedBufferSlotValue)
+ {
+ OnMetricWritten(value);
+ }
+ }
+
+ _bufferedValuesIndex = 0;
+ }
+
+ #endregion // Buffer Management
+
+ #region Statistics Calculation
+
+ // Statistics
+ private int _count;
+ private float _sum;
+ private float _sumSquared;
+ private float _min;
+ private float _max;
+
+ private void OnMetricWritten(float value)
+ {
+ _sum += value;
+ _sumSquared += value * value;
+ if (_count == 0 || value > _max)
+ {
+ _max = value;
+ }
+
+ if (_count == 0 || value < _min)
+ {
+ _min = value;
+ }
+
+ _count++;
+ }
+
+ internal EventCounterPayload GetEventCounterPayload()
+ {
+ lock (_bufferedValues)
+ {
+ Flush();
+ EventCounterPayload result = new EventCounterPayload();
+ result.Name = _name;
+ result.Count = _count;
+ result.Mean = _sum / _count;
+ result.StandardDerivation = (float)Math.Sqrt(_sumSquared / _count - _sum * _sum / _count / _count);
+ result.Min = _min;
+ result.Max = _max;
+ ResetStatistics();
+ return result;
+ }
+ }
+
+ private void ResetStatistics()
+ {
+ _count = 0;
+ _sum = 0;
+ _sumSquared = 0;
+ _min = 0;
+ _max = 0;
+ }
+
+ #endregion // Statistics Calculation
+
+ #endregion // private implementation
+ }
+
+ #region internal supporting classes
+
+ [EventData]
+ internal class EventCounterPayload : IEnumerable<KeyValuePair<string, object>>
+ {
+ public string Name { get; set; }
+
+ public float Mean { get; set; }
+
+ public float StandardDerivation { get; set; }
+
+ public int Count { get; set; }
+
+ public float Min { get; set; }
+
+ public float Max { get; set; }
+
+ public float IntervalSec { get; internal set; }
+
+ #region Implementation of the IEnumerable interface
+
+ public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
+ {
+ return ForEnumeration.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ForEnumeration.GetEnumerator();
+ }
+
+ private IEnumerable<KeyValuePair<string, object>> ForEnumeration
+ {
+ get
+ {
+ yield return new KeyValuePair<string, object>("Name", Name);
+ yield return new KeyValuePair<string, object>("Mean", Mean);
+ yield return new KeyValuePair<string, object>("StandardDerivation", StandardDerivation);
+ yield return new KeyValuePair<string, object>("Count", Count);
+ yield return new KeyValuePair<string, object>("Min", Min);
+ yield return new KeyValuePair<string, object>("Max", Max);
+ }
+ }
+
+ #endregion // Implementation of the IEnumerable interface
+ }
+
+ internal class EventCounterGroup : IDisposable
+ {
+ private readonly EventSource _eventSource;
+ private readonly int _eventSourceIndex;
+ private readonly List<EventCounter> _eventCounters;
+
+ internal EventCounterGroup(EventSource eventSource, int eventSourceIndex)
+ {
+ _eventSource = eventSource;
+ _eventSourceIndex = eventSourceIndex;
+ _eventCounters = new List<EventCounter>();
+ RegisterCommandCallback();
+ }
+
+ private void Add(EventCounter eventCounter)
+ {
+ _eventCounters.Add(eventCounter);
+ }
+
+ #region EventSource Command Processing
+
+ private void RegisterCommandCallback()
+ {
+ _eventSource.EventCommandExecuted += OnEventSourceCommand;
+ }
+
+ private void OnEventSourceCommand(object sender, EventCommandEventArgs e)
+ {
+ if (e.Command == EventCommand.Enable || e.Command == EventCommand.Update)
+ {
+ string valueStr;
+ float value;
+ if (e.Arguments.TryGetValue("EventCounterIntervalSec", out valueStr) && float.TryParse(valueStr, out value))
+ {
+ EnableTimer(value);
+ }
+ }
+ }
+
+ #endregion // EventSource Command Processing
+
+ #region Global EventCounterGroup Array management
+
+ private static EventCounterGroup[] s_eventCounterGroups;
+
+ internal static void AddEventCounter(EventSource eventSource, EventCounter eventCounter)
+ {
+ int eventSourceIndex = EventListener.EventSourceIndex(eventSource);
+
+ EventCounterGroup.EnsureEventSourceIndexAvailable(eventSourceIndex);
+ EventCounterGroup eventCounterGroup = GetEventCounterGroup(eventSource);
+ eventCounterGroup.Add(eventCounter);
+ }
+
+ private static void EnsureEventSourceIndexAvailable(int eventSourceIndex)
+ {
+ if (EventCounterGroup.s_eventCounterGroups == null)
+ {
+ EventCounterGroup.s_eventCounterGroups = new EventCounterGroup[eventSourceIndex + 1];
+ }
+ else if (eventSourceIndex >= EventCounterGroup.s_eventCounterGroups.Length)
+ {
+ EventCounterGroup[] newEventCounterGroups = new EventCounterGroup[eventSourceIndex + 1];
+ Array.Copy(EventCounterGroup.s_eventCounterGroups, 0, newEventCounterGroups, 0, EventCounterGroup.s_eventCounterGroups.Length);
+ EventCounterGroup.s_eventCounterGroups = newEventCounterGroups;
+ }
+ }
+
+ private static EventCounterGroup GetEventCounterGroup(EventSource eventSource)
+ {
+ int eventSourceIndex = EventListener.EventSourceIndex(eventSource);
+ EventCounterGroup result = EventCounterGroup.s_eventCounterGroups[eventSourceIndex];
+ if (result == null)
+ {
+ result = new EventCounterGroup(eventSource, eventSourceIndex);
+ EventCounterGroup.s_eventCounterGroups[eventSourceIndex] = result;
+ }
+
+ return result;
+ }
+
+ #endregion // Global EventCounterGroup Array management
+
+ #region Timer Processing
+
+ private DateTime _timeStampSinceCollectionStarted;
+ private int _pollingIntervalInMilliseconds;
+ private Timer _pollingTimer;
+
+ private void EnableTimer(float pollingIntervalInSeconds)
+ {
+ if (pollingIntervalInSeconds == 0)
+ {
+ if (_pollingTimer != null)
+ {
+ _pollingTimer.Dispose();
+ _pollingTimer = null;
+ }
+
+ _pollingIntervalInMilliseconds = 0;
+ }
+ else if (_pollingIntervalInMilliseconds == 0 || pollingIntervalInSeconds < _pollingIntervalInMilliseconds)
+ {
+ _pollingIntervalInMilliseconds = (int)(pollingIntervalInSeconds * 1000);
+ if (_pollingTimer != null)
+ {
+ _pollingTimer.Dispose();
+ _pollingTimer = null;
+ }
+
+ _timeStampSinceCollectionStarted = DateTime.Now;
+ _pollingTimer = new Timer(OnTimer, null, _pollingIntervalInMilliseconds, _pollingIntervalInMilliseconds);
+ }
+ }
+
+ private void OnTimer(object state)
+ {
+ if (_eventSource.IsEnabled())
+ {
+ DateTime now = DateTime.Now;
+ TimeSpan elapsed = now - _timeStampSinceCollectionStarted;
+ lock (_pollingTimer)
+ {
+ foreach (var eventCounter in _eventCounters)
+ {
+ EventCounterPayload payload = eventCounter.GetEventCounterPayload();
+ payload.IntervalSec = (float)elapsed.TotalSeconds;
+ _eventSource.Write("EventCounters", new EventSourceOptions() { Level = EventLevel.LogAlways }, new { Payload = payload });
+ }
+
+
+ _timeStampSinceCollectionStarted = now;
+ }
+ }
+ else
+ {
+ _pollingTimer.Dispose();
+ _pollingTimer = null;
+ EventCounterGroup.s_eventCounterGroups[_eventSourceIndex] = null;
+ }
+ }
+
+ #region PCL timer hack
+
+#if ES_BUILD_PCL
+ internal delegate void TimerCallback(object state);
+
+ internal sealed class Timer : CancellationTokenSource, IDisposable
+ {
+ private int _period;
+ private TimerCallback _callback;
+ private object _state;
+
+ internal Timer(TimerCallback callback, object state, int dueTime, int period)
+ {
+ _callback = callback;
+ _state = state;
+ _period = period;
+ Schedule(dueTime);
+ }
+
+ private void Schedule(int dueTime)
+ {
+ Task.Delay(dueTime, Token).ContinueWith(OnTimer, null, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
+ }
+
+ private void OnTimer(Task t, object s)
+ {
+ Schedule(_period);
+ _callback(_state);
+ }
+
+ public new void Dispose() { base.Cancel(); }
+ }
+#endif
+ #endregion // PCL timer hack
+
+ #endregion // Timer Processing
+
+ #region Implementation of the IDisposable interface
+
+ private bool _disposed = false;
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ if (_pollingTimer != null)
+ {
+ _pollingTimer.Dispose();
+ _pollingTimer = null;
+ }
+ }
+
+ _disposed = true;
+ }
+
+ #endregion // Implementation of the IDisposable interface
+ }
+
+ #endregion // internal supporting classes
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventDescriptor.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventDescriptor.cs
new file mode 100644
index 0000000000..8fb471a99f
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventDescriptor.cs
@@ -0,0 +1,209 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.InteropServices;
+
+#if ES_BUILD_STANDALONE
+using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
+#endif
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ [StructLayout(LayoutKind.Explicit, Size = 16)]
+#if !CORECLR && !ES_BUILD_PN
+ [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
+#endif // !CORECLR && !ES_BUILD_PN
+
+ /*
+ EventDescriptor was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0),
+ now the move to CoreLib marked them as private.
+ While they are technically private (it's a contract used between the library and the ILC toolchain),
+ we need them to be rooted and exported from shared library for the system to work.
+ For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to
+ root them and modify shared library definition to force export them.
+ */
+#if ES_BUILD_PN
+ public
+#else
+ internal
+#endif
+ struct EventDescriptor
+ {
+ # region private
+ [FieldOffset(0)]
+ private int m_traceloggingId;
+ [FieldOffset(0)]
+ private ushort m_id;
+ [FieldOffset(2)]
+ private byte m_version;
+ [FieldOffset(3)]
+ private byte m_channel;
+ [FieldOffset(4)]
+ private byte m_level;
+ [FieldOffset(5)]
+ private byte m_opcode;
+ [FieldOffset(6)]
+ private ushort m_task;
+ [FieldOffset(8)]
+ private long m_keywords;
+ #endregion
+
+ public EventDescriptor(
+ int traceloggingId,
+ byte level,
+ byte opcode,
+ long keywords
+ )
+ {
+ this.m_id = 0;
+ this.m_version = 0;
+ this.m_channel = 0;
+ this.m_traceloggingId = traceloggingId;
+ this.m_level = level;
+ this.m_opcode = opcode;
+ this.m_task = 0;
+ this.m_keywords = keywords;
+ }
+
+ public EventDescriptor(
+ int id,
+ byte version,
+ byte channel,
+ byte level,
+ byte opcode,
+ int task,
+ long keywords
+ )
+ {
+ if (id < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+
+ if (id > ushort.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue));
+ }
+
+ m_traceloggingId = 0;
+ m_id = (ushort)id;
+ m_version = version;
+ m_channel = channel;
+ m_level = level;
+ m_opcode = opcode;
+ m_keywords = keywords;
+
+ if (task < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
+ }
+
+ if (task > ushort.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue));
+ }
+
+ m_task = (ushort)task;
+ }
+
+ public int EventId
+ {
+ get
+ {
+ return m_id;
+ }
+ }
+ public byte Version
+ {
+ get
+ {
+ return m_version;
+ }
+ }
+ public byte Channel
+ {
+ get
+ {
+ return m_channel;
+ }
+ }
+ public byte Level
+ {
+ get
+ {
+ return m_level;
+ }
+ }
+ public byte Opcode
+ {
+ get
+ {
+ return m_opcode;
+ }
+ }
+ public int Task
+ {
+ get
+ {
+ return m_task;
+ }
+ }
+ public long Keywords
+ {
+ get
+ {
+ return m_keywords;
+ }
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is EventDescriptor))
+ return false;
+
+ return Equals((EventDescriptor) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return m_id ^ m_version ^ m_channel ^ m_level ^ m_opcode ^ m_task ^ (int)m_keywords;
+ }
+
+ public bool Equals(EventDescriptor other)
+ {
+ if ((m_id != other.m_id) ||
+ (m_version != other.m_version) ||
+ (m_channel != other.m_channel) ||
+ (m_level != other.m_level) ||
+ (m_opcode != other.m_opcode) ||
+ (m_task != other.m_task) ||
+ (m_keywords != other.m_keywords))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public static bool operator ==(EventDescriptor event1, EventDescriptor event2)
+ {
+ return event1.Equals(event2);
+ }
+
+ public static bool operator !=(EventDescriptor event1, EventDescriptor event2)
+ {
+ return !event1.Equals(event2);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs
new file mode 100644
index 0000000000..57d550dc8c
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs
@@ -0,0 +1,1207 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using Microsoft.Win32;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Runtime.InteropServices;
+using System.Security;
+#if !CORECLR && !ES_BUILD_PN
+using System.Security.Permissions;
+#endif // !CORECLR && !ES_BUILD_PN
+using System.Threading;
+using System;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+#if ES_BUILD_AGAINST_DOTNET_V35
+using Microsoft.Internal; // for Tuple (can't define alias for open generic types so we "use" the whole namespace)
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ // New in CLR4.0
+ internal enum ControllerCommand
+ {
+ // Strictly Positive numbers are for provider-specific commands, negative number are for 'shared' commands. 256
+ // The first 256 negative numbers are reserved for the framework.
+ Update = 0, // Not used by EventPrividerBase.
+ SendManifest = -1,
+ Enable = -2,
+ Disable = -3,
+ };
+
+ /// <summary>
+ /// Only here because System.Diagnostics.EventProvider needs one more extensibility hook (when it gets a
+ /// controller callback)
+ /// </summary>
+#if !CORECLR && !ES_BUILD_PN
+ [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
+#endif // !CORECLR && !ES_BUILD_PN
+ internal partial class EventProvider : IDisposable
+ {
+ // This is the windows EVENT_DATA_DESCRIPTOR structure. We expose it because this is what
+ // subclasses of EventProvider use when creating efficient (but unsafe) version of
+ // EventWrite. We do make it a nested type because we really don't expect anyone to use
+ // it except subclasses (and then only rarely).
+ public struct EventData
+ {
+ internal unsafe ulong Ptr;
+ internal uint Size;
+ internal uint Reserved;
+ }
+
+ /// <summary>
+ /// A struct characterizing ETW sessions (identified by the etwSessionId) as
+ /// activity-tracing-aware or legacy. A session that's activity-tracing-aware
+ /// has specified one non-zero bit in the reserved range 44-47 in the
+ /// 'allKeywords' value it passed in for a specific EventProvider.
+ /// </summary>
+ public struct SessionInfo
+ {
+ internal int sessionIdBit; // the index of the bit used for tracing in the "reserved" field of AllKeywords
+ internal int etwSessionId; // the machine-wide ETW session ID
+
+ internal SessionInfo(int sessionIdBit_, int etwSessionId_)
+ { sessionIdBit = sessionIdBit_; etwSessionId = etwSessionId_; }
+ }
+
+ private static bool m_setInformationMissing;
+
+ UnsafeNativeMethods.ManifestEtw.EtwEnableCallback m_etwCallback; // Trace Callback function
+ private long m_regHandle; // Trace Registration Handle
+ private byte m_level; // Tracing Level
+ private long m_anyKeywordMask; // Trace Enable Flags
+ private long m_allKeywordMask; // Match all keyword
+ private List<SessionInfo> m_liveSessions; // current live sessions (Tuple<sessionIdBit, etwSessionId>)
+ private bool m_enabled; // Enabled flag from Trace callback
+ private Guid m_providerId; // Control Guid
+ internal bool m_disposed; // when true provider has unregistered
+
+ [ThreadStatic]
+ private static WriteEventErrorCode s_returnCode; // The last return code
+
+ private const int s_basicTypeAllocationBufferSize = 16;
+ private const int s_etwMaxNumberArguments = 128;
+ private const int s_etwAPIMaxRefObjCount = 8;
+ private const int s_maxEventDataDescriptors = 128;
+ private const int s_traceEventMaximumSize = 65482;
+ private const int s_traceEventMaximumStringSize = 32724;
+
+ [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
+ public enum WriteEventErrorCode : int
+ {
+ //check mapping to runtime codes
+ NoError = 0,
+ NoFreeBuffers = 1,
+ EventTooBig = 2,
+ NullInput = 3,
+ TooManyArgs = 4,
+ Other = 5,
+ };
+
+ // Because callbacks happen on registration, and we need the callbacks for those setup
+ // we can't call Register in the constructor.
+ //
+ // Note that EventProvider should ONLY be used by EventSource. In particular because
+ // it registers a callback from native code you MUST dispose it BEFORE shutdown, otherwise
+ // you may get native callbacks during shutdown when we have destroyed the delegate.
+ // EventSource has special logic to do this, no one else should be calling EventProvider.
+ internal EventProvider()
+ {
+ }
+
+ /// <summary>
+ /// This method registers the controlGuid of this class with ETW. We need to be running on
+ /// Vista or above. If not a PlatformNotSupported exception will be thrown. If for some
+ /// reason the ETW Register call failed a NotSupported exception will be thrown.
+ /// </summary>
+ // <SecurityKernel Critical="True" Ring="0">
+ // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventRegister(System.Guid&,Microsoft.Win32.UnsafeNativeMethods.ManifestEtw+EtwEnableCallback,System.Void*,System.Int64&):System.UInt32" />
+ // <SatisfiesLinkDemand Name="Win32Exception..ctor(System.Int32)" />
+ // <ReferencesCritical Name="Method: EtwEnableCallBack(Guid&, Int32, Byte, Int64, Int64, Void*, Void*):Void" Ring="1" />
+ // </SecurityKernel>
+ internal unsafe void Register(Guid providerGuid)
+ {
+ m_providerId = providerGuid;
+ uint status;
+ m_etwCallback = new UnsafeNativeMethods.ManifestEtw.EtwEnableCallback(EtwEnableCallBack);
+
+ status = EventRegister(ref m_providerId, m_etwCallback);
+ if (status != 0)
+ {
+ throw new ArgumentException(Win32Native.GetMessage(unchecked((int)status)));
+ }
+ }
+
+ //
+ // implement Dispose Pattern to early deregister from ETW insted of waiting for
+ // the finalizer to call deregistration.
+ // Once the user is done with the provider it needs to call Close() or Dispose()
+ // If neither are called the finalizer will unregister the provider anyway
+ //
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ // <SecurityKernel Critical="True" TreatAsSafe="Does not expose critical resource" Ring="1">
+ // <ReferencesCritical Name="Method: Deregister():Void" Ring="1" />
+ // </SecurityKernel>
+ protected virtual void Dispose(bool disposing)
+ {
+ //
+ // explicit cleanup is done by calling Dispose with true from
+ // Dispose() or Close(). The disposing arguement is ignored because there
+ // are no unmanaged resources.
+ // The finalizer calls Dispose with false.
+ //
+
+ //
+ // check if the object has been allready disposed
+ //
+ if (m_disposed) return;
+
+ // Disable the provider.
+ m_enabled = false;
+
+ // Do most of the work under a lock to avoid shutdown race.
+
+ long registrationHandle = 0;
+ lock (EventListener.EventListenersLock)
+ {
+ // Double check
+ if (m_disposed)
+ return;
+
+ registrationHandle = m_regHandle;
+ m_regHandle = 0;
+ m_disposed = true;
+ }
+
+ // We do the Unregistration outside the EventListenerLock because there is a lock
+ // inside the ETW routines. This lock is taken before ETW issues commands
+ // Thus the ETW lock gets taken first and then our EventListenersLock gets taken
+ // in SendCommand(), and also here. If we called EventUnregister after taking
+ // the EventListenersLock then the take-lock order is reversed and we can have
+ // deadlocks in race conditions (dispose racing with an ETW command).
+ //
+ // We solve by Unregistering after releasing the EventListenerLock.
+ if (registrationHandle != 0)
+ EventUnregister(registrationHandle);
+
+ }
+
+ /// <summary>
+ /// This method deregisters the controlGuid of this class with ETW.
+ ///
+ /// </summary>
+ public virtual void Close()
+ {
+ Dispose();
+ }
+
+ ~EventProvider()
+ {
+ Dispose(false);
+ }
+
+ // <SecurityKernel Critical="True" Ring="0">
+ // <UsesUnsafeCode Name="Parameter filterData of type: Void*" />
+ // <UsesUnsafeCode Name="Parameter callbackContext of type: Void*" />
+ // </SecurityKernel>
+ unsafe void EtwEnableCallBack(
+ [In] ref System.Guid sourceId,
+ [In] int controlCode,
+ [In] byte setLevel,
+ [In] long anyKeyword,
+ [In] long allKeyword,
+ [In] UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData,
+ [In] void* callbackContext
+ )
+ {
+ // This is an optional callback API. We will therefore ignore any failures that happen as a
+ // result of turning on this provider as to not crash the app.
+ // EventSource has code to validate whether initialization it expected to occur actually occurred
+ try
+ {
+ ControllerCommand command = ControllerCommand.Update;
+ IDictionary<string, string> args = null;
+ bool skipFinalOnControllerCommand = false;
+ if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_ENABLE_PROVIDER)
+ {
+ m_enabled = true;
+ m_level = setLevel;
+ m_anyKeywordMask = anyKeyword;
+ m_allKeywordMask = allKeyword;
+
+ // ES_SESSION_INFO is a marker for additional places we #ifdeffed out to remove
+ // references to EnumerateTraceGuidsEx. This symbol is actually not used because
+ // today we use FEATURE_ACTIVITYSAMPLING to determine if this code is there or not.
+ // However we put it in the #if so that we don't lose the fact that this feature
+ // switch is at least partially independent of FEATURE_ACTIVITYSAMPLING
+
+ List<Tuple<SessionInfo, bool>> sessionsChanged = GetSessions();
+ foreach (var session in sessionsChanged)
+ {
+ int sessionChanged = session.Item1.sessionIdBit;
+ int etwSessionId = session.Item1.etwSessionId;
+ bool bEnabling = session.Item2;
+
+ skipFinalOnControllerCommand = true;
+ args = null; // reinitialize args for every session...
+
+ // if we get more than one session changed we have no way
+ // of knowing which one "filterData" belongs to
+ if (sessionsChanged.Count > 1)
+ filterData = null;
+
+ // read filter data only when a session is being *added*
+ byte[] data;
+ int keyIndex;
+ if (bEnabling &&
+ GetDataFromController(etwSessionId, filterData, out command, out data, out keyIndex))
+ {
+ args = new Dictionary<string, string>(4);
+ while (keyIndex < data.Length)
+ {
+ int keyEnd = FindNull(data, keyIndex);
+ int valueIdx = keyEnd + 1;
+ int valueEnd = FindNull(data, valueIdx);
+ if (valueEnd < data.Length)
+ {
+ string key = System.Text.Encoding.UTF8.GetString(data, keyIndex, keyEnd - keyIndex);
+ string value = System.Text.Encoding.UTF8.GetString(data, valueIdx, valueEnd - valueIdx);
+ args[key] = value;
+ }
+ keyIndex = valueEnd + 1;
+ }
+ }
+
+ // execute OnControllerCommand once for every session that has changed.
+ OnControllerCommand(command, args, (bEnabling ? sessionChanged : -sessionChanged), etwSessionId);
+ }
+ }
+ else if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_DISABLE_PROVIDER)
+ {
+ m_enabled = false;
+ m_level = 0;
+ m_anyKeywordMask = 0;
+ m_allKeywordMask = 0;
+ m_liveSessions = null;
+ }
+ else if (controlCode == UnsafeNativeMethods.ManifestEtw.EVENT_CONTROL_CODE_CAPTURE_STATE)
+ {
+ command = ControllerCommand.SendManifest;
+ }
+ else
+ return; // per spec you ignore commands you don't recognize.
+
+ if (!skipFinalOnControllerCommand)
+ OnControllerCommand(command, args, 0, 0);
+ }
+ catch (Exception)
+ {
+ // We want to ignore any failures that happen as a result of turning on this provider as to
+ // not crash the app.
+ }
+ }
+
+ // New in CLR4.0
+ protected virtual void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments, int sessionId, int etwSessionId) { }
+ protected EventLevel Level { get { return (EventLevel)m_level; } set { m_level = (byte)value; } }
+ protected EventKeywords MatchAnyKeyword { get { return (EventKeywords)m_anyKeywordMask; } set { m_anyKeywordMask = unchecked((long)value); } }
+ protected EventKeywords MatchAllKeyword { get { return (EventKeywords)m_allKeywordMask; } set { m_allKeywordMask = unchecked((long)value); } }
+
+ static private int FindNull(byte[] buffer, int idx)
+ {
+ while (idx < buffer.Length && buffer[idx] != 0)
+ idx++;
+ return idx;
+ }
+
+ /// <summary>
+ /// Determines the ETW sessions that have been added and/or removed to the set of
+ /// sessions interested in the current provider. It does so by (1) enumerating over all
+ /// ETW sessions that enabled 'this.m_Guid' for the current process ID, and (2)
+ /// comparing the current list with a list it cached on the previous invocation.
+ ///
+ /// The return value is a list of tuples, where the SessionInfo specifies the
+ /// ETW session that was added or remove, and the bool specifies whether the
+ /// session was added or whether it was removed from the set.
+ /// </summary>
+ private List<Tuple<SessionInfo, bool>> GetSessions()
+ {
+ List<SessionInfo> liveSessionList = null;
+
+ GetSessionInfo(
+ (int etwSessionId, long matchAllKeywords, ref List<SessionInfo> sessionList) =>
+ GetSessionInfoCallback(etwSessionId, matchAllKeywords, ref sessionList),
+ ref liveSessionList);
+
+ List<Tuple<SessionInfo, bool>> changedSessionList = new List<Tuple<SessionInfo, bool>>();
+
+ // first look for sessions that have gone away (or have changed)
+ // (present in the m_liveSessions but not in the new liveSessionList)
+ if (m_liveSessions != null)
+ {
+ foreach (SessionInfo s in m_liveSessions)
+ {
+ int idx;
+ if ((idx = IndexOfSessionInList(liveSessionList, s.etwSessionId)) < 0 ||
+ (liveSessionList[idx].sessionIdBit != s.sessionIdBit))
+ changedSessionList.Add(Tuple.Create(s, false));
+
+ }
+ }
+ // next look for sessions that were created since the last callback (or have changed)
+ // (present in the new liveSessionList but not in m_liveSessions)
+ if (liveSessionList != null)
+ {
+ foreach (SessionInfo s in liveSessionList)
+ {
+ int idx;
+ if ((idx = IndexOfSessionInList(m_liveSessions, s.etwSessionId)) < 0 ||
+ (m_liveSessions[idx].sessionIdBit != s.sessionIdBit))
+ changedSessionList.Add(Tuple.Create(s, true));
+ }
+ }
+
+ m_liveSessions = liveSessionList;
+ return changedSessionList;
+ }
+
+
+ /// <summary>
+ /// This method is the callback used by GetSessions() when it calls into GetSessionInfo().
+ /// It updates a List{SessionInfo} based on the etwSessionId and matchAllKeywords that
+ /// GetSessionInfo() passes in.
+ /// </summary>
+ private static void GetSessionInfoCallback(int etwSessionId, long matchAllKeywords,
+ ref List<SessionInfo> sessionList)
+ {
+ uint sessionIdBitMask = (uint)SessionMask.FromEventKeywords(unchecked((ulong)matchAllKeywords));
+ // an ETW controller that specifies more than the mandated bit for our EventSource
+ // will be ignored...
+ if (bitcount(sessionIdBitMask) > 1)
+ return;
+
+ if (sessionList == null)
+ sessionList = new List<SessionInfo>(8);
+
+ if (bitcount(sessionIdBitMask) == 1)
+ {
+ // activity-tracing-aware etw session
+ sessionList.Add(new SessionInfo(bitindex(sessionIdBitMask) + 1, etwSessionId));
+ }
+ else
+ {
+ // legacy etw session
+ sessionList.Add(new SessionInfo(bitcount((uint)SessionMask.All) + 1, etwSessionId));
+ }
+ }
+
+ private delegate void SessionInfoCallback(int etwSessionId, long matchAllKeywords, ref List<SessionInfo> sessionList);
+
+ /// <summary>
+ /// This method enumerates over all active ETW sessions that have enabled 'this.m_Guid'
+ /// for the current process ID, calling 'action' for each session, and passing it the
+ /// ETW session and the 'AllKeywords' the session enabled for the current provider.
+ /// </summary>
+ private unsafe void GetSessionInfo(SessionInfoCallback action, ref List<SessionInfo> sessionList)
+ {
+ // We wish the EventSource package to be legal for Windows Store applications.
+ // Currently EnumerateTraceGuidsEx is not an allowed API, so we avoid its use here
+ // and use the information in the registry instead. This means that ETW controllers
+ // that do not publish their intent to the registry (basically all controllers EXCEPT
+ // TraceEventSesion) will not work properly
+
+ // However the framework version of EventSource DOES have ES_SESSION_INFO defined and thus
+ // does not have this issue.
+#if ES_SESSION_INFO || !ES_BUILD_STANDALONE
+ int buffSize = 256; // An initial guess that probably works most of the time.
+ byte* buffer;
+ for (; ; )
+ {
+ var space = stackalloc byte[buffSize];
+ buffer = space;
+ var hr = 0;
+
+ fixed (Guid* provider = &m_providerId)
+ {
+ hr = UnsafeNativeMethods.ManifestEtw.EnumerateTraceGuidsEx(UnsafeNativeMethods.ManifestEtw.TRACE_QUERY_INFO_CLASS.TraceGuidQueryInfo,
+ provider, sizeof(Guid), buffer, buffSize, ref buffSize);
+ }
+ if (hr == 0)
+ break;
+ if (hr != 122 /* ERROR_INSUFFICIENT_BUFFER */)
+ return;
+ }
+
+ var providerInfos = (UnsafeNativeMethods.ManifestEtw.TRACE_GUID_INFO*)buffer;
+ var providerInstance = (UnsafeNativeMethods.ManifestEtw.TRACE_PROVIDER_INSTANCE_INFO*)&providerInfos[1];
+ int processId = unchecked((int)Win32Native.GetCurrentProcessId());
+ // iterate over the instances of the EventProvider in all processes
+ for (int i = 0; i < providerInfos->InstanceCount; i++)
+ {
+ if (providerInstance->Pid == processId)
+ {
+ var enabledInfos = (UnsafeNativeMethods.ManifestEtw.TRACE_ENABLE_INFO*)&providerInstance[1];
+ // iterate over the list of active ETW sessions "listening" to the current provider
+ for (int j = 0; j < providerInstance->EnableCount; j++)
+ action(enabledInfos[j].LoggerId, enabledInfos[j].MatchAllKeyword, ref sessionList);
+ }
+ if (providerInstance->NextOffset == 0)
+ break;
+ Debug.Assert(0 <= providerInstance->NextOffset && providerInstance->NextOffset < buffSize);
+ var structBase = (byte*)providerInstance;
+ providerInstance = (UnsafeNativeMethods.ManifestEtw.TRACE_PROVIDER_INSTANCE_INFO*)&structBase[providerInstance->NextOffset];
+ }
+#else
+#if !ES_BUILD_PCL && !FEATURE_PAL // TODO command arguments don't work on PCL builds...
+ // This code is only used in the Nuget Package Version of EventSource. because
+ // the code above is using APIs baned from UWP apps.
+ //
+ // TODO: In addition to only working when TraceEventSession enables the provider, this code
+ // also has a problem because TraceEvent does not clean up if the registry is stale
+ // It is unclear if it is worth keeping, but for now we leave it as it does work
+ // at least some of the time.
+
+ // Determine our session from what is in the registry.
+ string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}";
+ if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8)
+ regKey = @"Software" + @"\Wow6432Node" + regKey;
+ else
+ regKey = @"Software" + regKey;
+
+ var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(regKey);
+ if (key != null)
+ {
+ foreach (string valueName in key.GetValueNames())
+ {
+ if (valueName.StartsWith("ControllerData_Session_", StringComparison.Ordinal))
+ {
+ string strId = valueName.Substring(23); // strip of the ControllerData_Session_
+ int etwSessionId;
+ if (int.TryParse(strId, out etwSessionId))
+ {
+ // we need to assert this permission for partial trust scenarios
+ (new RegistryPermission(RegistryPermissionAccess.Read, regKey)).Assert();
+ var data = key.GetValue(valueName) as byte[];
+ if (data != null)
+ {
+ var dataAsString = System.Text.Encoding.UTF8.GetString(data);
+ int keywordIdx = dataAsString.IndexOf("EtwSessionKeyword", StringComparison.Ordinal);
+ if (0 <= keywordIdx)
+ {
+ int startIdx = keywordIdx + 18;
+ int endIdx = dataAsString.IndexOf('\0', startIdx);
+ string keywordBitString = dataAsString.Substring(startIdx, endIdx-startIdx);
+ int keywordBit;
+ if (0 < endIdx && int.TryParse(keywordBitString, out keywordBit))
+ action(etwSessionId, 1L << keywordBit, ref sessionList);
+ }
+ }
+ }
+ }
+ }
+ }
+#endif
+#endif
+ }
+
+ /// <summary>
+ /// Returns the index of the SesisonInfo from 'sessions' that has the specified 'etwSessionId'
+ /// or -1 if the value is not present.
+ /// </summary>
+ private static int IndexOfSessionInList(List<SessionInfo> sessions, int etwSessionId)
+ {
+ if (sessions == null)
+ return -1;
+ // for non-coreclr code we could use List<T>.FindIndex(Predicate<T>), but we need this to compile
+ // on coreclr as well
+ for (int i = 0; i < sessions.Count; ++i)
+ if (sessions[i].etwSessionId == etwSessionId)
+ return i;
+
+ return -1;
+ }
+
+ /// <summary>
+ /// Gets any data to be passed from the controller to the provider. It starts with what is passed
+ /// into the callback, but unfortunately this data is only present for when the provider is active
+ /// at the time the controller issues the command. To allow for providers to activate after the
+ /// controller issued a command, we also check the registry and use that to get the data. The function
+ /// returns an array of bytes representing the data, the index into that byte array where the data
+ /// starts, and the command being issued associated with that data.
+ /// </summary>
+ private unsafe bool GetDataFromController(int etwSessionId,
+ UnsafeNativeMethods.ManifestEtw.EVENT_FILTER_DESCRIPTOR* filterData, out ControllerCommand command, out byte[] data, out int dataStart)
+ {
+ data = null;
+ dataStart = 0;
+ if (filterData == null)
+ {
+#if (!ES_BUILD_PCL && !ES_BUILD_PN && !FEATURE_PAL)
+ string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}";
+ if (Marshal.SizeOf(typeof(IntPtr)) == 8)
+ regKey = @"Software" + @"\Wow6432Node" + regKey;
+ else
+ regKey = @"Software" + regKey;
+
+ string valueName = "ControllerData_Session_" + etwSessionId.ToString(CultureInfo.InvariantCulture);
+
+ using (RegistryKey key = Registry.LocalMachine.OpenSubKey(regKey, writable: false))
+ {
+ data = key.GetValue(valueName) as byte[];
+ }
+
+ if (data != null)
+ {
+ // We only used the persisted data from the registry for updates.
+ command = ControllerCommand.Update;
+ return true;
+ }
+#endif
+ }
+ else
+ {
+ if (filterData->Ptr != 0 && 0 < filterData->Size && filterData->Size <= 1024)
+ {
+ data = new byte[filterData->Size];
+ Marshal.Copy((IntPtr)filterData->Ptr, data, 0, data.Length);
+ }
+ command = (ControllerCommand)filterData->Type;
+ return true;
+ }
+
+ command = ControllerCommand.Update;
+ return false;
+ }
+
+ /// <summary>
+ /// IsEnabled, method used to test if provider is enabled
+ /// </summary>
+ public bool IsEnabled()
+ {
+ return m_enabled;
+ }
+
+ /// <summary>
+ /// IsEnabled, method used to test if event is enabled
+ /// </summary>
+ /// <param name="level">
+ /// Level to test
+ /// </param>
+ /// <param name="keywords">
+ /// Keyword to test
+ /// </param>
+ public bool IsEnabled(byte level, long keywords)
+ {
+ //
+ // If not enabled at all, return false.
+ //
+ if (!m_enabled)
+ {
+ return false;
+ }
+
+ // This also covers the case of Level == 0.
+ if ((level <= m_level) ||
+ (m_level == 0))
+ {
+
+ //
+ // Check if Keyword is enabled
+ //
+
+ if ((keywords == 0) ||
+ (((keywords & m_anyKeywordMask) != 0) &&
+ ((keywords & m_allKeywordMask) == m_allKeywordMask)))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ public static WriteEventErrorCode GetLastWriteEventError()
+ {
+ return s_returnCode;
+ }
+
+ //
+ // Helper function to set the last error on the thread
+ //
+ private static void SetLastError(int error)
+ {
+ switch (error)
+ {
+ case UnsafeNativeMethods.ManifestEtw.ERROR_ARITHMETIC_OVERFLOW:
+ case UnsafeNativeMethods.ManifestEtw.ERROR_MORE_DATA:
+ s_returnCode = WriteEventErrorCode.EventTooBig;
+ break;
+ case UnsafeNativeMethods.ManifestEtw.ERROR_NOT_ENOUGH_MEMORY:
+ s_returnCode = WriteEventErrorCode.NoFreeBuffers;
+ break;
+ }
+ }
+
+ // <SecurityKernel Critical="True" Ring="0">
+ // <UsesUnsafeCode Name="Local intptrPtr of type: IntPtr*" />
+ // <UsesUnsafeCode Name="Local intptrPtr of type: Int32*" />
+ // <UsesUnsafeCode Name="Local longptr of type: Int64*" />
+ // <UsesUnsafeCode Name="Local uintptr of type: UInt32*" />
+ // <UsesUnsafeCode Name="Local ulongptr of type: UInt64*" />
+ // <UsesUnsafeCode Name="Local charptr of type: Char*" />
+ // <UsesUnsafeCode Name="Local byteptr of type: Byte*" />
+ // <UsesUnsafeCode Name="Local shortptr of type: Int16*" />
+ // <UsesUnsafeCode Name="Local sbyteptr of type: SByte*" />
+ // <UsesUnsafeCode Name="Local ushortptr of type: UInt16*" />
+ // <UsesUnsafeCode Name="Local floatptr of type: Single*" />
+ // <UsesUnsafeCode Name="Local doubleptr of type: Double*" />
+ // <UsesUnsafeCode Name="Local boolptr of type: Boolean*" />
+ // <UsesUnsafeCode Name="Local guidptr of type: Guid*" />
+ // <UsesUnsafeCode Name="Local decimalptr of type: Decimal*" />
+ // <UsesUnsafeCode Name="Local booleanptr of type: Boolean*" />
+ // <UsesUnsafeCode Name="Parameter dataDescriptor of type: EventData*" />
+ // <UsesUnsafeCode Name="Parameter dataBuffer of type: Byte*" />
+ // </SecurityKernel>
+ private static unsafe object EncodeObject(ref object data, ref EventData* dataDescriptor, ref byte* dataBuffer, ref uint totalEventSize)
+ /*++
+
+ Routine Description:
+
+ This routine is used by WriteEvent to unbox the object type and
+ to fill the passed in ETW data descriptor.
+
+ Arguments:
+
+ data - argument to be decoded
+
+ dataDescriptor - pointer to the descriptor to be filled (updated to point to the next empty entry)
+
+ dataBuffer - storage buffer for storing user data, needed because cant get the address of the object
+ (updated to point to the next empty entry)
+
+ Return Value:
+
+ null if the object is a basic type other than string or byte[]. String otherwise
+
+ --*/
+ {
+ Again:
+ dataDescriptor->Reserved = 0;
+
+ string sRet = data as string;
+ byte[] blobRet = null;
+
+ if (sRet != null)
+ {
+ dataDescriptor->Size = ((uint)sRet.Length + 1) * 2;
+ }
+ else if ((blobRet = data as byte[]) != null)
+ {
+ // first store array length
+ *(int*)dataBuffer = blobRet.Length;
+ dataDescriptor->Ptr = (ulong)dataBuffer;
+ dataDescriptor->Size = 4;
+ totalEventSize += dataDescriptor->Size;
+
+ // then the array parameters
+ dataDescriptor++;
+ dataBuffer += s_basicTypeAllocationBufferSize;
+ dataDescriptor->Size = (uint)blobRet.Length;
+ }
+ else if (data is IntPtr)
+ {
+ dataDescriptor->Size = (uint)sizeof(IntPtr);
+ IntPtr* intptrPtr = (IntPtr*)dataBuffer;
+ *intptrPtr = (IntPtr)data;
+ dataDescriptor->Ptr = (ulong)intptrPtr;
+ }
+ else if (data is int)
+ {
+ dataDescriptor->Size = (uint)sizeof(int);
+ int* intptr = (int*)dataBuffer;
+ *intptr = (int)data;
+ dataDescriptor->Ptr = (ulong)intptr;
+ }
+ else if (data is long)
+ {
+ dataDescriptor->Size = (uint)sizeof(long);
+ long* longptr = (long*)dataBuffer;
+ *longptr = (long)data;
+ dataDescriptor->Ptr = (ulong)longptr;
+ }
+ else if (data is uint)
+ {
+ dataDescriptor->Size = (uint)sizeof(uint);
+ uint* uintptr = (uint*)dataBuffer;
+ *uintptr = (uint)data;
+ dataDescriptor->Ptr = (ulong)uintptr;
+ }
+ else if (data is UInt64)
+ {
+ dataDescriptor->Size = (uint)sizeof(ulong);
+ UInt64* ulongptr = (ulong*)dataBuffer;
+ *ulongptr = (ulong)data;
+ dataDescriptor->Ptr = (ulong)ulongptr;
+ }
+ else if (data is char)
+ {
+ dataDescriptor->Size = (uint)sizeof(char);
+ char* charptr = (char*)dataBuffer;
+ *charptr = (char)data;
+ dataDescriptor->Ptr = (ulong)charptr;
+ }
+ else if (data is byte)
+ {
+ dataDescriptor->Size = (uint)sizeof(byte);
+ byte* byteptr = (byte*)dataBuffer;
+ *byteptr = (byte)data;
+ dataDescriptor->Ptr = (ulong)byteptr;
+ }
+ else if (data is short)
+ {
+ dataDescriptor->Size = (uint)sizeof(short);
+ short* shortptr = (short*)dataBuffer;
+ *shortptr = (short)data;
+ dataDescriptor->Ptr = (ulong)shortptr;
+ }
+ else if (data is sbyte)
+ {
+ dataDescriptor->Size = (uint)sizeof(sbyte);
+ sbyte* sbyteptr = (sbyte*)dataBuffer;
+ *sbyteptr = (sbyte)data;
+ dataDescriptor->Ptr = (ulong)sbyteptr;
+ }
+ else if (data is ushort)
+ {
+ dataDescriptor->Size = (uint)sizeof(ushort);
+ ushort* ushortptr = (ushort*)dataBuffer;
+ *ushortptr = (ushort)data;
+ dataDescriptor->Ptr = (ulong)ushortptr;
+ }
+ else if (data is float)
+ {
+ dataDescriptor->Size = (uint)sizeof(float);
+ float* floatptr = (float*)dataBuffer;
+ *floatptr = (float)data;
+ dataDescriptor->Ptr = (ulong)floatptr;
+ }
+ else if (data is double)
+ {
+ dataDescriptor->Size = (uint)sizeof(double);
+ double* doubleptr = (double*)dataBuffer;
+ *doubleptr = (double)data;
+ dataDescriptor->Ptr = (ulong)doubleptr;
+ }
+ else if (data is bool)
+ {
+ // WIN32 Bool is 4 bytes
+ dataDescriptor->Size = 4;
+ int* intptr = (int*)dataBuffer;
+ if (((bool)data))
+ {
+ *intptr = 1;
+ }
+ else
+ {
+ *intptr = 0;
+ }
+ dataDescriptor->Ptr = (ulong)intptr;
+ }
+ else if (data is Guid)
+ {
+ dataDescriptor->Size = (uint)sizeof(Guid);
+ Guid* guidptr = (Guid*)dataBuffer;
+ *guidptr = (Guid)data;
+ dataDescriptor->Ptr = (ulong)guidptr;
+ }
+ else if (data is decimal)
+ {
+ dataDescriptor->Size = (uint)sizeof(decimal);
+ decimal* decimalptr = (decimal*)dataBuffer;
+ *decimalptr = (decimal)data;
+ dataDescriptor->Ptr = (ulong)decimalptr;
+ }
+ else if (data is DateTime)
+ {
+ const long UTCMinTicks = 504911232000000000;
+ long dateTimeTicks = 0;
+ // We cannot translate dates sooner than 1/1/1601 in UTC.
+ // To avoid getting an ArgumentOutOfRangeException we compare with 1/1/1601 DateTime ticks
+ if (((DateTime)data).Ticks > UTCMinTicks)
+ dateTimeTicks = ((DateTime)data).ToFileTimeUtc();
+ dataDescriptor->Size = (uint)sizeof(long);
+ long* longptr = (long*)dataBuffer;
+ *longptr = dateTimeTicks;
+ dataDescriptor->Ptr = (ulong)longptr;
+ }
+ else
+ {
+ if (data is System.Enum)
+ {
+ Type underlyingType = Enum.GetUnderlyingType(data.GetType());
+ if (underlyingType == typeof(int))
+ {
+#if !ES_BUILD_PCL
+ data = ((IConvertible)data).ToInt32(null);
+#else
+ data = (int)data;
+#endif
+ goto Again;
+ }
+ else if (underlyingType == typeof(long))
+ {
+#if !ES_BUILD_PCL
+ data = ((IConvertible)data).ToInt64(null);
+#else
+ data = (long)data;
+#endif
+ goto Again;
+ }
+ }
+
+ // To our eyes, everything else is a just a string
+ if (data == null)
+ sRet = "";
+ else
+ sRet = data.ToString();
+ dataDescriptor->Size = ((uint)sRet.Length + 1) * 2;
+ }
+
+ totalEventSize += dataDescriptor->Size;
+
+ // advance buffers
+ dataDescriptor++;
+ dataBuffer += s_basicTypeAllocationBufferSize;
+
+ return (object)sRet ?? (object)blobRet;
+ }
+
+ /// <summary>
+ /// WriteEvent, method to write a parameters with event schema properties
+ /// </summary>
+ /// <param name="eventDescriptor">
+ /// Event Descriptor for this event.
+ /// </param>
+ /// <param name="activityID">
+ /// A pointer to the activity ID GUID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// childActivityID is marked as 'related' to the current activity ID.
+ /// </param>
+ /// <param name="eventPayload">
+ /// Payload for the ETW event.
+ /// </param>
+ // <SecurityKernel Critical="True" Ring="0">
+ // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventWrite(System.Int64,EventDescriptor&,System.UInt32,System.Void*):System.UInt32" />
+ // <UsesUnsafeCode Name="Local dataBuffer of type: Byte*" />
+ // <UsesUnsafeCode Name="Local pdata of type: Char*" />
+ // <UsesUnsafeCode Name="Local userData of type: EventData*" />
+ // <UsesUnsafeCode Name="Local userDataPtr of type: EventData*" />
+ // <UsesUnsafeCode Name="Local currentBuffer of type: Byte*" />
+ // <UsesUnsafeCode Name="Local v0 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v1 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v2 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v3 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v4 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v5 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v6 of type: Char*" />
+ // <UsesUnsafeCode Name="Local v7 of type: Char*" />
+ // <ReferencesCritical Name="Method: EncodeObject(Object&, EventData*, Byte*):String" Ring="1" />
+ // </SecurityKernel>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Performance-critical code")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
+ internal unsafe bool WriteEvent(ref EventDescriptor eventDescriptor, Guid* activityID, Guid* childActivityID, params object[] eventPayload)
+ {
+ int status = 0;
+
+ if (IsEnabled(eventDescriptor.Level, eventDescriptor.Keywords))
+ {
+ int argCount = 0;
+ unsafe
+ {
+ argCount = eventPayload.Length;
+
+ if (argCount > s_etwMaxNumberArguments)
+ {
+ s_returnCode = WriteEventErrorCode.TooManyArgs;
+ return false;
+ }
+
+ uint totalEventSize = 0;
+ int index;
+ int refObjIndex = 0;
+ List<int> refObjPosition = new List<int>(s_etwAPIMaxRefObjCount);
+ List<object> dataRefObj = new List<object>(s_etwAPIMaxRefObjCount);
+ EventData* userData = stackalloc EventData[2 * argCount];
+ EventData* userDataPtr = (EventData*)userData;
+ byte* dataBuffer = stackalloc byte[s_basicTypeAllocationBufferSize * 2 * argCount]; // Assume 16 chars for non-string argument
+ byte* currentBuffer = dataBuffer;
+
+ //
+ // The loop below goes through all the arguments and fills in the data
+ // descriptors. For strings save the location in the dataString array.
+ // Calculates the total size of the event by adding the data descriptor
+ // size value set in EncodeObject method.
+ //
+ bool hasNonStringRefArgs = false;
+ for (index = 0; index < eventPayload.Length; index++)
+ {
+ if (eventPayload[index] != null)
+ {
+ object supportedRefObj;
+ supportedRefObj = EncodeObject(ref eventPayload[index], ref userDataPtr, ref currentBuffer, ref totalEventSize);
+
+ if (supportedRefObj != null)
+ {
+ // EncodeObject advanced userDataPtr to the next empty slot
+ int idx = (int)(userDataPtr - userData - 1);
+ if (!(supportedRefObj is string))
+ {
+ if (eventPayload.Length + idx + 1 - index > s_etwMaxNumberArguments)
+ {
+ s_returnCode = WriteEventErrorCode.TooManyArgs;
+ return false;
+ }
+ hasNonStringRefArgs = true;
+ }
+ dataRefObj.Add(supportedRefObj);
+ refObjPosition.Add(idx);
+ refObjIndex++;
+ }
+ }
+ else
+ {
+ s_returnCode = WriteEventErrorCode.NullInput;
+ return false;
+ }
+ }
+
+ // update argCount based on actual number of arguments written to 'userData'
+ argCount = (int)(userDataPtr - userData);
+
+ if (totalEventSize > s_traceEventMaximumSize)
+ {
+ s_returnCode = WriteEventErrorCode.EventTooBig;
+ return false;
+ }
+
+ // the optimized path (using "fixed" instead of allocating pinned GCHandles
+ if (!hasNonStringRefArgs && (refObjIndex < s_etwAPIMaxRefObjCount))
+ {
+ // Fast path: at most 8 string arguments
+
+ // ensure we have at least s_etwAPIMaxStringCount in dataString, so that
+ // the "fixed" statement below works
+ while (refObjIndex < s_etwAPIMaxRefObjCount)
+ {
+ dataRefObj.Add(null);
+ ++refObjIndex;
+ }
+
+ //
+ // now fix any string arguments and set the pointer on the data descriptor
+ //
+ fixed (char* v0 = (string)dataRefObj[0], v1 = (string)dataRefObj[1], v2 = (string)dataRefObj[2], v3 = (string)dataRefObj[3],
+ v4 = (string)dataRefObj[4], v5 = (string)dataRefObj[5], v6 = (string)dataRefObj[6], v7 = (string)dataRefObj[7])
+ {
+ userDataPtr = (EventData*)userData;
+ if (dataRefObj[0] != null)
+ {
+ userDataPtr[refObjPosition[0]].Ptr = (ulong)v0;
+ }
+ if (dataRefObj[1] != null)
+ {
+ userDataPtr[refObjPosition[1]].Ptr = (ulong)v1;
+ }
+ if (dataRefObj[2] != null)
+ {
+ userDataPtr[refObjPosition[2]].Ptr = (ulong)v2;
+ }
+ if (dataRefObj[3] != null)
+ {
+ userDataPtr[refObjPosition[3]].Ptr = (ulong)v3;
+ }
+ if (dataRefObj[4] != null)
+ {
+ userDataPtr[refObjPosition[4]].Ptr = (ulong)v4;
+ }
+ if (dataRefObj[5] != null)
+ {
+ userDataPtr[refObjPosition[5]].Ptr = (ulong)v5;
+ }
+ if (dataRefObj[6] != null)
+ {
+ userDataPtr[refObjPosition[6]].Ptr = (ulong)v6;
+ }
+ if (dataRefObj[7] != null)
+ {
+ userDataPtr[refObjPosition[7]].Ptr = (ulong)v7;
+ }
+
+ status = UnsafeNativeMethods.ManifestEtw.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, argCount, userData);
+ }
+ }
+ else
+ {
+ // Slow path: use pinned handles
+ userDataPtr = (EventData*)userData;
+
+ GCHandle[] rgGCHandle = new GCHandle[refObjIndex];
+ for (int i = 0; i < refObjIndex; ++i)
+ {
+ // below we still use "fixed" to avoid taking dependency on the offset of the first field
+ // in the object (the way we would need to if we used GCHandle.AddrOfPinnedObject)
+ rgGCHandle[i] = GCHandle.Alloc(dataRefObj[i], GCHandleType.Pinned);
+ if (dataRefObj[i] is string)
+ {
+ fixed (char* p = (string)dataRefObj[i])
+ userDataPtr[refObjPosition[i]].Ptr = (ulong)p;
+ }
+ else
+ {
+ fixed (byte* p = (byte[])dataRefObj[i])
+ userDataPtr[refObjPosition[i]].Ptr = (ulong)p;
+ }
+ }
+
+ status = UnsafeNativeMethods.ManifestEtw.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, argCount, userData);
+
+ for (int i = 0; i < refObjIndex; ++i)
+ {
+ rgGCHandle[i].Free();
+ }
+ }
+ }
+ }
+
+ if (status != 0)
+ {
+ SetLastError((int)status);
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// WriteEvent, method to be used by generated code on a derived class
+ /// </summary>
+ /// <param name="eventDescriptor">
+ /// Event Descriptor for this event.
+ /// </param>
+ /// <param name="activityID">
+ /// A pointer to the activity ID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// If this event is generating a child activity (WriteEventTransfer related activity) this is child activity
+ /// This can be null for events that do not generate a child activity.
+ /// </param>
+ /// <param name="dataCount">
+ /// number of event descriptors
+ /// </param>
+ /// <param name="data">
+ /// pointer do the event data
+ /// </param>
+ // <SecurityKernel Critical="True" Ring="0">
+ // <CallsSuppressUnmanagedCode Name="UnsafeNativeMethods.ManifestEtw.EventWrite(System.Int64,EventDescriptor&,System.UInt32,System.Void*):System.UInt32" />
+ // </SecurityKernel>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
+ internal unsafe protected bool WriteEvent(ref EventDescriptor eventDescriptor, Guid* activityID, Guid* childActivityID, int dataCount, IntPtr data)
+ {
+ if (childActivityID != null)
+ {
+ // activity transfers are supported only for events that specify the Send or Receive opcode
+ Debug.Assert((EventOpcode)eventDescriptor.Opcode == EventOpcode.Send ||
+ (EventOpcode)eventDescriptor.Opcode == EventOpcode.Receive ||
+ (EventOpcode)eventDescriptor.Opcode == EventOpcode.Start ||
+ (EventOpcode)eventDescriptor.Opcode == EventOpcode.Stop);
+ }
+
+ int status = UnsafeNativeMethods.ManifestEtw.EventWriteTransferWrapper(m_regHandle, ref eventDescriptor, activityID, childActivityID, dataCount, (EventData*)data);
+
+ if (status != 0)
+ {
+ SetLastError(status);
+ return false;
+ }
+ return true;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
+ internal unsafe bool WriteEventRaw(
+ ref EventDescriptor eventDescriptor,
+ Guid* activityID,
+ Guid* relatedActivityID,
+ int dataCount,
+ IntPtr data)
+ {
+ int status;
+
+ status = UnsafeNativeMethods.ManifestEtw.EventWriteTransferWrapper(
+ m_regHandle,
+ ref eventDescriptor,
+ activityID,
+ relatedActivityID,
+ dataCount,
+ (EventData*)data);
+
+ if (status != 0)
+ {
+ SetLastError(status);
+ return false;
+ }
+ return true;
+ }
+
+
+ // These are look-alikes to the Manifest based ETW OS APIs that have been shimmed to work
+ // either with Manifest ETW or Classic ETW (if Manifest based ETW is not available).
+ private unsafe uint EventRegister(ref Guid providerId, UnsafeNativeMethods.ManifestEtw.EtwEnableCallback enableCallback)
+ {
+ m_providerId = providerId;
+ m_etwCallback = enableCallback;
+ return UnsafeNativeMethods.ManifestEtw.EventRegister(ref providerId, enableCallback, null, ref m_regHandle);
+ }
+
+ private uint EventUnregister(long registrationHandle)
+ {
+ return UnsafeNativeMethods.ManifestEtw.EventUnregister(registrationHandle);
+ }
+
+ static int[] nibblebits = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
+ private static int bitcount(uint n)
+ {
+ int count = 0;
+ for (; n != 0; n = n >> 4)
+ count += nibblebits[n & 0x0f];
+ return count;
+ }
+ private static int bitindex(uint n)
+ {
+ Debug.Assert(bitcount(n) == 1);
+ int idx = 0;
+ while ((n & (1 << idx)) == 0)
+ idx++;
+ return idx;
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs
new file mode 100644
index 0000000000..cf4901de6f
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSource.cs
@@ -0,0 +1,6942 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
+// It is available from http://www.codeplex.com/hyperAddin
+#if PLATFORM_WINDOWS
+
+#define FEATURE_MANAGED_ETW
+
+#if !ES_BUILD_STANDALONE && !CORECLR && !ES_BUILD_PN
+#define FEATURE_ACTIVITYSAMPLING
+#endif // !ES_BUILD_STANDALONE
+
+#endif // PLATFORM_WINDOWS
+
+#if ES_BUILD_STANDALONE
+#define FEATURE_MANAGED_ETW_CHANNELS
+// #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+#endif
+
+/* DESIGN NOTES DESIGN NOTES DESIGN NOTES DESIGN NOTES */
+// DESIGN NOTES
+// Over the years EventSource has become more complex and so it is important to understand
+// the basic structure of the code to insure that it does not grow more complex.
+//
+// Basic Model
+//
+// PRINCIPLE: EventSource - ETW decoupling
+//
+// Conceptually and EventSouce is something takes event logging data from the source methods
+// To the EventListener that can subscribe them. Note that CONCEPTUALLY EVENTSOURCES DON'T
+// KNOW ABOUT ETW!. The MODEL of the system is that there is a special EventListern Which
+// we will call the EtwEventListener, that forwards commands from ETW to EventSources and
+// listeners to the EventSources and forwards on those events to ETW. THus the model should
+// be that you DON'T NEED ETW.
+//
+// Now in actual practice, EventSouce have rather intimate knowledge of ETW and send events
+// to it directly, but this can be VIEWED AS AN OPTIMIATION.
+//
+// Basic Event Data Flow:
+//
+// There are two ways for event Data to enter the system
+// 1) WriteEvent* and friends. This is called the 'contract' based approach because
+// you write a method per event which forms a contract that is know at compile time.
+// In this scheme each event is given an EVENTID (small integer). which is its identity
+// 2) Write<T> methods. This is called the 'dynamic' approach because new events
+// can be created on the fly. Event identity is determined by the event NAME, and these
+// are not quite as efficient at runtime since you have at least a hash table lookup
+// on every event write.
+//
+// EventSource-EventListener transfer fully support both ways of writing events (either contract
+// based (WriteEvent*) or dynamic (Write<T>). Both way fully support the same set of data
+// types. It is suggested, however, that you use the contract based approach when the event scheme
+// is known at compile time (that is whenever possible). It is more efficient, but more importantly
+// it makes the contract very explicit, and centralizes all policy about logging. These are good
+// things. The Write<T> API is really meant for more ad-hoc
+//
+// Allowed Data.
+//
+// Note that EventSource-EventListeners have a conceptual serialization-deserialization that happens
+// during the transfer. In particular object identity is not preserved, some objects are morphed,
+// and not all data types are supported. In particular you can pass
+//
+// A Valid type to log to an EventSource include
+// * Primitive data types
+// * IEnumerable<T> of valid types T (this include arrays) (* New for V4.6)
+// * Explicitly Opted in class or struct with public property Getters over Valid types. (* New for V4.6)
+//
+// This set of types is roughly a generalization of JSON support (Basically primitives, bags, and arrays).
+//
+// Explicitly allowed structs include (* New for V4.6)
+// * Marked with the EventData attribute
+// * implicitly defined (e.g the C# new {x = 3, y = 5} syntax)
+// * KeyValuePair<K,V> (thus dictionaries can be passed since they are an IEnumerable of KeyValuePair)
+//
+// When classes are returned in an EventListener, what is returned is something that implements
+// IDictionary<string, T>. Thus when objects are passed to an EventSource they are transformed
+// into a key-value bag (the IDictionary<string, T>) for consumption in the listener. These
+// are obvious NOT the original objects.
+//
+// ETWserialization formats:
+//
+// As mentioned conceptually EventSource's send data to EventListeners and there is a conceptual
+// copy/morph of that data as described above. In addition the .NET framework supports a conceptual
+// ETWListener that will send the data to then ETW stream. If you use this feature, the data needs
+// to be serialized in a way that ETW supports. ETW supports the following serialization formats
+//
+// 1) Manifest Based serialization.
+// 2) SelfDescribing serialization (TraceLogging style in the TraceLogging directory)
+//
+// A key factor is that the Write<T> method, which support on the fly definition of events, can't
+// support the manifest based serialization because the manifest needs the schema of all events
+// to be known before any events are emitted. This implies the following
+//
+// If you use Write<T> and the output goes to ETW it will use the SelfDescribing format.
+// If you use the EventSource(string) constructor for an eventSource (in which you don't
+// create a subclass), the default is also to use Self-Describing serialization. In addition
+// you can use the EventSoruce(EventSourceSettings) constructor to also explicitly specify
+// Self-Describing serialization format. These effect the WriteEvent* APIs going to ETW.
+//
+// Note that none of this ETW serialization logic affects EventListeners. Only the ETW listener.
+//
+// *************************************************************************************
+// *** INTERNALS: Event Propagation
+//
+// Data enters the system either though
+//
+// 1) A user defined method in the user defined subclass of EventSource which calls
+// A) A typesafe type specific overload of WriteEvent(ID, ...) e.g. WriteEvent(ID, string, string)
+// * which calls into the unsafe WriteEventCore(ID COUNT EventData*) WriteEventWithRelatedActivityIdCore()
+// B) The typesafe overload WriteEvent(ID, object[]) which calls the private helper WriteEventVarargs(ID, Guid* object[])
+// C) Directly into the unsafe WriteEventCore(ID, COUNT EventData*) or WriteEventWithRelatedActivityIdCore()
+//
+// All event data eventually flows to one of
+// * WriteEventWithRelatedActivityIdCore(ID, Guid*, COUNT, EventData*)
+// * WriteEventVarargs(ID, Guid*, object[])
+//
+// 2) A call to one of the overloads of Write<T>. All these overloads end up in
+// * WriteImpl<T>(EventName, Options, Data, Guid*, Guid*)
+//
+// On output there are the following routines
+// Writing to all listeners that are NOT ETW, we have the following routines
+// * WriteToAllListeners(ID, Guid*, COUNT, EventData*)
+// * WriteToAllListeners(ID, Guid*, object[])
+// * WriteToAllListeners(NAME, Guid*, EventPayload)
+//
+// EventPayload is the internal type that implements the IDictionary<string, object> interface
+// The EventListeners will pass back for serialized classes for nested object, but
+// WriteToAllListeners(NAME, Guid*, EventPayload) unpacks this uses the fields as if they
+// were parameters to a method.
+//
+// The first two are used for the WriteEvent* case, and the later is used for the Write<T> case.
+//
+// Writing to ETW, Manifest Based
+// EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
+// EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
+// Writing to ETW, Self-Describing format
+// WriteMultiMerge(NAME, Options, Types, EventData*)
+// WriteMultiMerge(NAME, Options, Types, object[])
+// WriteImpl<T> has logic that knows how to serialize (like WriteMultiMerge) but also knows
+// will write it to
+//
+// All ETW writes eventually call
+// EventWriteTransfer (native PINVOKE wrapper)
+// EventWriteTransferWrapper (fixes compat problem if you pass null as the related activityID)
+// EventProvider.WriteEventRaw - sets last error
+// EventSource.WriteEventRaw - Does EventSource exception handling logic
+// WriteMultiMerge
+// WriteImpl<T>
+// EventProvider.WriteEvent(EventDescriptor, Guid*, COUNT, EventData*)
+// EventProvider.WriteEvent(EventDescriptor, Guid*, object[])
+//
+// Serialization: We have a bit of a hodge-podge of serializers right now. Only the one for ETW knows
+// how to deal with nested classes or arrays. I will call this serializer the 'TypeInfo' serializer
+// since it is the TraceLoggingTypeInfo structure that knows how to do this. Effectively for a type you
+// can call one of these
+// WriteMetadata - transforms the type T into serialization meta data blob for that type
+// WriteObjectData - transforms an object of T into serialization meta data blob for that type
+// GetData - transforms an object of T into its deserialized form suitable for passing to EventListener.
+// The first two are used to serialize something for ETW. The second one is used to transform the object
+// for use by the EventListener. We also have a 'DecodeObject' method that will take a EventData* and
+// deserialize to pass to an EventListener, but it only works on primitive types (types supported in version V4.5).
+//
+// It is an important observation that while EventSource does support users directly calling with EventData*
+// blobs, we ONLY support that for the primitive types (V4.5 level support). Thus while there is a EventData*
+// path through the system it is only for some types. The object[] path is the more general (but less efficient) path.
+//
+// TODO There is cleanup needed There should be no divergence until WriteEventRaw.
+//
+// TODO: We should have a single choke point (right now we always have this parallel EventData* and object[] path. This
+// was historical (at one point we tried to pass object directly from EventSoruce to EventListener. That was always
+// fragile and a compatibility headache, but we have finally been forced into the idea that there is always a transformation.
+// This allows us to use the EventData* form to be the canonical data format in the low level APIs. This also gives us the
+// opportunity to expose this format to EventListeners in the future.
+//
+using System;
+using System.Runtime.CompilerServices;
+#if FEATURE_ACTIVITYSAMPLING
+using System.Collections.Concurrent;
+#endif
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using System.Reflection;
+using System.Resources;
+using System.Security;
+#if !CORECLR && !ES_BUILD_PN
+using System.Security.Permissions;
+#endif // !CORECLR && !ES_BUILD_PN
+
+using System.Text;
+using System.Threading;
+using Microsoft.Win32;
+
+#if ES_BUILD_STANDALONE
+using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
+#else
+using System.Threading.Tasks;
+#endif
+
+using Microsoft.Reflection;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+#if CORECLR || ES_BUILD_PN
+using Internal.Runtime.Augments;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// This class is meant to be inherited by a user-defined event source in order to define a managed
+ /// ETW provider. Please See DESIGN NOTES above for the internal architecture.
+ /// The minimal definition of an EventSource simply specifies a number of ETW event methods that
+ /// call one of the EventSource.WriteEvent overloads, <see cref="EventSource.WriteEventCore"/>,
+ /// or <see cref="EventSource.WriteEventWithRelatedActivityIdCore"/> to log them. This functionality
+ /// is sufficient for many users.
+ /// <para>
+ /// To achieve more control over the ETW provider manifest exposed by the event source type, the
+ /// [<see cref="EventAttribute"/>] attributes can be specified for the ETW event methods.
+ /// </para><para>
+ /// For very advanced EventSources, it is possible to intercept the commands being given to the
+ /// eventSource and change what filtering is done (see EventListener.EnableEvents and
+ /// <see cref="EventListener.DisableEvents"/>) or cause actions to be performed by the eventSource,
+ /// e.g. dumping a data structure (see EventSource.SendCommand and
+ /// <see cref="EventSource.OnEventCommand"/>).
+ /// </para><para>
+ /// The eventSources can be turned on with Windows ETW controllers (e.g. logman), immediately.
+ /// It is also possible to control and intercept the data dispatcher programmatically. See
+ /// <see cref="EventListener"/> for more.
+ /// </para>
+ /// </summary>
+ /// <remarks>
+ /// This is a minimal definition for a custom event source:
+ /// <code>
+ /// [EventSource(Name="Samples-Demos-Minimal")]
+ /// sealed class MinimalEventSource : EventSource
+ /// {
+ /// public static MinimalEventSource Log = new MinimalEventSource();
+ /// public void Load(long ImageBase, string Name) { WriteEvent(1, ImageBase, Name); }
+ /// public void Unload(long ImageBase) { WriteEvent(2, ImageBase); }
+ /// private MinimalEventSource() {}
+ /// }
+ /// </code>
+ /// </remarks>
+ public partial class EventSource : IDisposable
+ {
+
+#if FEATURE_EVENTSOURCE_XPLAT
+ private static readonly EventListener persistent_Xplat_Listener = XplatEventLogger.InitializePersistentListener();
+#endif //FEATURE_EVENTSOURCE_XPLAT
+
+ /// <summary>
+ /// The human-friendly name of the eventSource. It defaults to the simple name of the class
+ /// </summary>
+ public string Name { get { return m_name; } }
+ /// <summary>
+ /// Every eventSource is assigned a GUID to uniquely identify it to the system.
+ /// </summary>
+ public Guid Guid { get { return m_guid; } }
+
+ /// <summary>
+ /// Returns true if the eventSource has been enabled at all. This is the prefered test
+ /// to be performed before a relatively expensive EventSource operation.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ public bool IsEnabled()
+ {
+ return m_eventSourceEnabled;
+ }
+
+ /// <summary>
+ /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled.
+ ///
+ /// Note that the result of this function is only an approximation on whether a particular
+ /// event is active or not. It is only meant to be used as way of avoiding expensive
+ /// computation for logging when logging is not on, therefore it sometimes returns false
+ /// positives (but is always accurate when returning false). EventSources are free to
+ /// have additional filtering.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ public bool IsEnabled(EventLevel level, EventKeywords keywords)
+ {
+ return IsEnabled(level, keywords, EventChannel.None);
+ }
+
+ /// <summary>
+ /// Returns true if events with greater than or equal 'level' and have one of 'keywords' set are enabled, or
+ /// if 'keywords' specifies a channel bit for a channel that is enabled.
+ ///
+ /// Note that the result of this function only an approximation on whether a particular
+ /// event is active or not. It is only meant to be used as way of avoiding expensive
+ /// computation for logging when logging is not on, therefore it sometimes returns false
+ /// positives (but is always accurate when returning false). EventSources are free to
+ /// have additional filtering.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ public bool IsEnabled(EventLevel level, EventKeywords keywords, EventChannel channel)
+ {
+ if (!m_eventSourceEnabled)
+ return false;
+
+ if (!IsEnabledCommon(m_eventSourceEnabled, m_level, m_matchAnyKeyword, level, keywords, channel))
+ return false;
+
+#if !FEATURE_ACTIVITYSAMPLING
+
+ return true;
+
+#else // FEATURE_ACTIVITYSAMPLING
+
+ return true;
+
+#if OPTIMIZE_IS_ENABLED
+ //================================================================================
+ // 2013/03/06 - The code below is a possible optimization for IsEnabled(level, kwd)
+ // in case activity tracing/sampling is enabled. The added complexity of this
+ // code however weighs against having it "on" until we know it's really needed.
+ // For now we'll have this #ifdef-ed out in case we see evidence this is needed.
+ //================================================================================
+
+ // At this point we believe the event is enabled, however we now need to check
+ // if we filter because of activity
+
+ // Optimization, all activity filters also register a delegate here, so if there
+ // is no delegate, we know there are no activity filters, which means that there
+ // is no additional filtering, which means that we can return true immediately.
+ if (s_activityDying == null)
+ return true;
+
+ // if there's at least one legacy ETW listener we can't filter this
+ if (m_legacySessions != null && m_legacySessions.Count > 0)
+ return true;
+
+ // if any event ID that triggers a new activity, or "transfers" activities
+ // is covered by 'keywords' we can't filter this
+ if (unchecked(((long)keywords & m_keywordTriggers)) != 0)
+ return true;
+
+ // See if all listeners have activity filters that would block the event.
+ for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
+ if (etwSession == null)
+ continue;
+
+ ActivityFilter activityFilter = etwSession.m_activityFilter;
+ if (activityFilter == null ||
+ ActivityFilter.GetFilter(activityFilter, this) == null)
+ {
+ // No activity filter for ETW, if event is active for ETW, we can't filter.
+ for (int i = 0; i < m_eventData.Length; i++)
+ if (m_eventData[i].EnabledForETW)
+ return true;
+ }
+ else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
+ return true;
+ }
+
+ // for regular event listeners
+ var curDispatcher = m_Dispatchers;
+ while (curDispatcher != null)
+ {
+ ActivityFilter activityFilter = curDispatcher.m_Listener.m_activityFilter;
+ if (activityFilter == null)
+ {
+ // See if any event is enabled.
+ for (int i = 0; i < curDispatcher.m_EventEnabled.Length; i++)
+ if (curDispatcher.m_EventEnabled[i])
+ return true;
+ }
+ else if (ActivityFilter.IsCurrentActivityActive(activityFilter))
+ return true;
+ curDispatcher = curDispatcher.m_Next;
+ }
+
+ // Every listener has an activity filter that is blocking writing the event,
+ // thus the event is not enabled.
+ return false;
+#endif // OPTIMIZE_IS_ENABLED
+
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+
+ /// <summary>
+ /// Returns the settings for the event source instance
+ /// </summary>
+ public EventSourceSettings Settings
+ {
+ get { return m_config; }
+ }
+
+ // Manifest support
+ /// <summary>
+ /// Returns the GUID that uniquely identifies the eventSource defined by 'eventSourceType'.
+ /// This API allows you to compute this without actually creating an instance of the EventSource.
+ /// It only needs to reflect over the type.
+ /// </summary>
+ public static Guid GetGuid(Type eventSourceType)
+ {
+ if (eventSourceType == null)
+ throw new ArgumentNullException(nameof(eventSourceType));
+ Contract.EndContractBlock();
+
+ EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute));
+ string name = eventSourceType.Name;
+ if (attrib != null)
+ {
+ if (attrib.Guid != null)
+ {
+ Guid g = Guid.Empty;
+#if !ES_BUILD_AGAINST_DOTNET_V35
+ if (Guid.TryParse(attrib.Guid, out g))
+ return g;
+#else
+ try { return new Guid(attrib.Guid); }
+ catch (Exception) { }
+#endif
+ }
+
+ if (attrib.Name != null)
+ name = attrib.Name;
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentException(Resources.GetResourceString("Argument_InvalidTypeName"), nameof(eventSourceType));
+ }
+ return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive.
+ }
+ /// <summary>
+ /// Returns the official ETW Provider name for the eventSource defined by 'eventSourceType'.
+ /// This API allows you to compute this without actually creating an instance of the EventSource.
+ /// It only needs to reflect over the type.
+ /// </summary>
+ public static string GetName(Type eventSourceType)
+ {
+ return GetName(eventSourceType, EventManifestOptions.None);
+ }
+
+ /// <summary>
+ /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
+ /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
+ /// This is the preferred way of generating a manifest to be embedded in the ETW stream as it is fast and
+ /// the fact that it only includes localized entries for the current UI culture is an acceptable tradeoff.
+ /// </summary>
+ /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
+ /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
+ /// which it is embedded. This parameter specifies what name will be used</param>
+ /// <returns>The XML data string</returns>
+ public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest)
+ {
+ return GenerateManifest(eventSourceType, assemblyPathToIncludeInManifest, EventManifestOptions.None);
+ }
+ /// <summary>
+ /// Returns a string of the XML manifest associated with the eventSourceType. The scheme for this XML is
+ /// documented at in EventManifest Schema http://msdn.microsoft.com/en-us/library/aa384043(VS.85).aspx.
+ /// Pass EventManifestOptions.AllCultures when generating a manifest to be registered on the machine. This
+ /// ensures that the entries in the event log will be "optimally" localized.
+ /// </summary>
+ /// <param name="eventSourceType">The type of the event source class for which the manifest is generated</param>
+ /// <param name="assemblyPathToIncludeInManifest">The manifest XML fragment contains the string name of the DLL name in
+ /// which it is embedded. This parameter specifies what name will be used</param>
+ /// <param name="flags">The flags to customize manifest generation. If flags has bit OnlyIfNeededForRegistration specified
+ /// this returns null when the eventSourceType does not require explicit registration</param>
+ /// <returns>The XML data string or null</returns>
+ public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags)
+ {
+ if (eventSourceType == null)
+ throw new ArgumentNullException(nameof(eventSourceType));
+ Contract.EndContractBlock();
+
+ byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags);
+ return (manifestBytes == null) ? null : Encoding.UTF8.GetString(manifestBytes, 0, manifestBytes.Length);
+ }
+
+ // EventListener support
+ /// <summary>
+ /// returns a list (IEnumerable) of all sources in the appdomain). EventListeners typically need this.
+ /// </summary>
+ /// <returns></returns>
+ public static IEnumerable<EventSource> GetSources()
+ {
+ var ret = new List<EventSource>();
+ lock (EventListener.EventListenersLock)
+ {
+ foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
+ {
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource != null && !eventSource.IsDisposed)
+ ret.Add(eventSource);
+ }
+ }
+ return ret;
+ }
+
+ /// <summary>
+ /// Send a command to a particular EventSource identified by 'eventSource'.
+ /// Calling this routine simply forwards the command to the EventSource.OnEventCommand
+ /// callback. What the EventSource does with the command and its arguments are from
+ /// that point EventSource-specific.
+ /// </summary>
+ /// <param name="eventSource">The instance of EventSource to send the command to</param>
+ /// <param name="command">A positive user-defined EventCommand, or EventCommand.SendManifest</param>
+ /// <param name="commandArguments">A set of (name-argument, value-argument) pairs associated with the command</param>
+ public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments)
+ {
+ if (eventSource == null)
+ throw new ArgumentNullException(nameof(eventSource));
+
+ // User-defined EventCommands should not conflict with the reserved commands.
+ if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidCommand"), nameof(command));
+ }
+
+ eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments);
+ }
+
+#if !ES_BUILD_STANDALONE
+ /// <summary>
+ /// This property allows EventSource code to appropriately handle as "different"
+ /// activities started on different threads that have not had an activity created on them.
+ /// </summary>
+ internal static Guid InternalCurrentThreadActivityId
+ {
+ get
+ {
+ Guid retval = CurrentThreadActivityId;
+ if (retval == Guid.Empty)
+ {
+ retval = FallbackActivityId;
+ }
+ return retval;
+ }
+ }
+
+ internal static Guid FallbackActivityId
+ {
+ get
+ {
+#pragma warning disable 612, 618
+ int threadID = AppDomain.GetCurrentThreadId();
+
+ // Managed thread IDs are more aggressively re-used than native thread IDs,
+ // so we'll use the latter...
+ return new Guid(unchecked((uint)threadID),
+ unchecked((ushort)s_currentPid), unchecked((ushort)(s_currentPid >> 16)),
+ 0x94, 0x1b, 0x87, 0xd5, 0xa6, 0x5c, 0x36, 0x64);
+#pragma warning restore 612, 618
+ }
+ }
+#endif // !ES_BUILD_STANDALONE
+
+ // Error APIs. (We don't throw by default, but you can probe for status)
+ /// <summary>
+ /// Because
+ ///
+ /// 1) Logging is often optional and thus should not generate fatal errors (exceptions)
+ /// 2) EventSources are often initialized in class constructors (which propagate exceptions poorly)
+ ///
+ /// The event source constructor does not throw exceptions. Instead we remember any exception that
+ /// was generated (it is also logged to Trace.WriteLine).
+ /// </summary>
+ public Exception ConstructionException { get { return m_constructionException; } }
+
+ /// <summary>
+ /// EventSources can have arbitrary string key-value pairs associated with them called Traits.
+ /// These traits are not interpreted by the EventSource but may be interpreted by EventListeners
+ /// (e.g. like the built in ETW listener). These traits are specififed at EventSource
+ /// construction time and can be retrieved by using this GetTrait API.
+ /// </summary>
+ /// <param name="key">The key to look up in the set of key-value pairs passed to the EventSource constructor</param>
+ /// <returns>The value string associated iwth key. Will return null if there is no such key.</returns>
+ public string GetTrait(string key)
+ {
+ if (m_traits != null)
+ {
+ for (int i = 0; i < m_traits.Length - 1; i += 2)
+ {
+ if (m_traits[i] == key)
+ return m_traits[i + 1];
+ }
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Displays the name and GUID for the eventSource for debugging purposes.
+ /// </summary>
+ public override string ToString()
+ {
+ return Resources.GetResourceString("EventSource_ToString", Name, Guid);
+ }
+
+ /// <summary>
+ /// Fires when a Command (e.g. Enable) comes from a an EventListener.
+ /// </summary>
+ public event EventHandler<EventCommandEventArgs> EventCommandExecuted
+ {
+ add
+ {
+ m_eventCommandExecuted += value;
+
+ // If we have an EventHandler<EventCommandEventArgs> attached to the EventSource before the first command arrives
+ // It should get a chance to handle the deferred commands.
+ EventCommandEventArgs deferredCommands = m_deferredCommands;
+ while (deferredCommands != null)
+ {
+ value(this, deferredCommands);
+ deferredCommands = deferredCommands.nextCommand;
+ }
+ }
+ remove
+ {
+ m_eventCommandExecuted -= value;
+ }
+ }
+
+ #region protected
+ /// <summary>
+ /// This is the constructor that most users will use to create their eventSource. It takes
+ /// no parameters. The ETW provider name and GUID of the EventSource are determined by the EventSource
+ /// custom attribute (so you can determine these things declaratively). If the GUID for the eventSource
+ /// is not specified in the EventSourceAttribute (recommended), it is Generated by hashing the name.
+ /// If the ETW provider name of the EventSource is not given, the name of the EventSource class is used as
+ /// the ETW provider name.
+ /// </summary>
+ protected EventSource()
+ : this(EventSourceSettings.EtwManifestEventFormat)
+ {
+ }
+
+ /// <summary>
+ /// By default calling the 'WriteEvent' methods do NOT throw on errors (they silently discard the event).
+ /// This is because in most cases users assume logging is not 'precious' and do NOT wish to have logging failures
+ /// crash the program. However for those applications where logging is 'precious' and if it fails the caller
+ /// wishes to react, setting 'throwOnEventWriteErrors' will cause an exception to be thrown if WriteEvent
+ /// fails. Note the fact that EventWrite succeeds does not necessarily mean that the event reached its destination
+ /// only that operation of writing it did not fail. These EventSources will not generate self-describing ETW events.
+ ///
+ /// For compatibility only use the EventSourceSettings.ThrowOnEventWriteErrors flag instead.
+ /// </summary>
+ // [Obsolete("Use the EventSource(EventSourceSettings) overload")]
+ protected EventSource(bool throwOnEventWriteErrors)
+ : this(EventSourceSettings.EtwManifestEventFormat | (throwOnEventWriteErrors ? EventSourceSettings.ThrowOnEventWriteErrors : 0))
+ { }
+
+ /// <summary>
+ /// Construct an EventSource with additional non-default settings (see EventSourceSettings for more)
+ /// </summary>
+ protected EventSource(EventSourceSettings settings) : this(settings, null) { }
+
+ /// <summary>
+ /// Construct an EventSource with additional non-default settings.
+ ///
+ /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
+ /// The first string is the key and the second is the value. These are not interpreted by EventSource
+ /// itself but may be interprated the listeners. Can be fetched with GetTrait(string).
+ /// </summary>
+ /// <param name="settings">See EventSourceSettings for more.</param>
+ /// <param name="traits">A collection of key-value strings (must be an even number).</param>
+ protected EventSource(EventSourceSettings settings, params string[] traits)
+ {
+ m_config = ValidateSettings(settings);
+
+ Guid eventSourceGuid;
+ string eventSourceName;
+
+ EventMetadata[] eventDescriptors;
+ byte[] manifest;
+ GetMetadata(out eventSourceGuid, out eventSourceName, out eventDescriptors, out manifest);
+
+ if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null)
+ {
+ var myType = this.GetType();
+ eventSourceGuid = GetGuid(myType);
+ eventSourceName = GetName(myType);
+ }
+
+ Initialize(eventSourceGuid, eventSourceName, traits);
+ }
+
+ internal virtual void GetMetadata(out Guid eventSourceGuid, out string eventSourceName, out EventMetadata[] eventData, out byte[] manifestBytes)
+ {
+ //
+ // In ProjectN subclasses need to override this method, and return the data from their EventSourceAttribute and EventAttribute annotations.
+ // On other architectures it is a no-op.
+ //
+ // eventDescriptors needs to contain one EventDescriptor for each event; the event's ID should be the same as its index in this array.
+ // manifestBytes is a UTF-8 encoding of the ETW manifest for the type.
+ //
+ // This will be implemented by an IL rewriter, so we can't make this method abstract or the initial build of the subclass would fail.
+ //
+ eventSourceGuid = Guid.Empty;
+ eventSourceName = null;
+ eventData = null;
+ manifestBytes = null;
+
+ return;
+ }
+
+ /// <summary>
+ /// This method is called when the eventSource is updated by the controller.
+ /// </summary>
+ protected virtual void OnEventCommand(EventCommandEventArgs command) { }
+
+#pragma warning disable 1591
+ // optimized for common signatures (no args)
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId)
+ {
+ WriteEventCore(eventId, 0, null);
+ }
+
+ // optimized for common signatures (ints)
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, int arg1)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 4;
+ WriteEventCore(eventId, 1, descrs);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, int arg1, int arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, int arg1, int arg2, int arg3)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)(&arg3);
+ descrs[2].Size = 4;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+
+ // optimized for common signatures (longs)
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ WriteEventCore(eventId, 1, descrs);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1, long arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 8;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1, long arg2, long arg3)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 8;
+ descrs[2].DataPointer = (IntPtr)(&arg3);
+ descrs[2].Size = 8;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+
+ // optimized for common signatures (strings)
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ fixed (char* string1Bytes = arg1)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[1];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ WriteEventCore(eventId, 1, descrs);
+ }
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, string arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ if (arg2 == null) arg2 = "";
+ fixed (char* string1Bytes = arg1)
+ fixed (char* string2Bytes = arg2)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)string2Bytes;
+ descrs[1].Size = ((arg2.Length + 1) * 2);
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ if (arg2 == null) arg2 = "";
+ if (arg3 == null) arg3 = "";
+ fixed (char* string1Bytes = arg1)
+ fixed (char* string2Bytes = arg2)
+ fixed (char* string3Bytes = arg3)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)string2Bytes;
+ descrs[1].Size = ((arg2.Length + 1) * 2);
+ descrs[2].DataPointer = (IntPtr)string3Bytes;
+ descrs[2].Size = ((arg3.Length + 1) * 2);
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (string and ints)
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, int arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ fixed (char* string1Bytes = arg1)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, int arg2, int arg3)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ fixed (char* string1Bytes = arg1)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)(&arg3);
+ descrs[2].Size = 4;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (string and longs)
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg1 == null) arg1 = "";
+ fixed (char* string1Bytes = arg1)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)string1Bytes;
+ descrs[0].Size = ((arg1.Length + 1) * 2);
+ descrs[1].DataPointer = (IntPtr)(&arg2);
+ descrs[1].Size = 8;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (long and string)
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1, string arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg2 == null) arg2 = "";
+ fixed (char* string2Bytes = arg2)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ descrs[1].DataPointer = (IntPtr)string2Bytes;
+ descrs[1].Size = ((arg2.Length + 1) * 2);
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ // optimized for common signatures (int and string)
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, int arg1, string arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ if (arg2 == null) arg2 = "";
+ fixed (char* string2Bytes = arg2)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)string2Bytes;
+ descrs[1].Size = ((arg2.Length + 1) * 2);
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, byte[] arg1)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ if (arg1 == null || arg1.Length == 0)
+ {
+ int blobSize = 0;
+ descrs[0].DataPointer = (IntPtr)(&blobSize);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty content
+ descrs[1].Size = 0;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ else
+ {
+ int blobSize = arg1.Length;
+ fixed (byte* blob = &arg1[0])
+ {
+ descrs[0].DataPointer = (IntPtr)(&blobSize);
+ descrs[0].Size = 4;
+ descrs[1].DataPointer = (IntPtr)blob;
+ descrs[1].Size = blobSize;
+ WriteEventCore(eventId, 2, descrs);
+ }
+ }
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, long arg1, byte[] arg2)
+ {
+ if (m_eventSourceEnabled)
+ {
+ EventSource.EventData* descrs = stackalloc EventSource.EventData[3];
+ descrs[0].DataPointer = (IntPtr)(&arg1);
+ descrs[0].Size = 8;
+ if (arg2 == null || arg2.Length == 0)
+ {
+ int blobSize = 0;
+ descrs[1].DataPointer = (IntPtr)(&blobSize);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)(&blobSize); // valid address instead of empty contents
+ descrs[2].Size = 0;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ else
+ {
+ int blobSize = arg2.Length;
+ fixed (byte* blob = &arg2[0])
+ {
+ descrs[1].DataPointer = (IntPtr)(&blobSize);
+ descrs[1].Size = 4;
+ descrs[2].DataPointer = (IntPtr)blob;
+ descrs[2].Size = blobSize;
+ WriteEventCore(eventId, 3, descrs);
+ }
+ }
+ }
+ }
+
+#pragma warning restore 1591
+
+ /// <summary>
+ /// Used to construct the data structure to be passed to the native ETW APIs - EventWrite and EventWriteTransfer.
+ /// </summary>
+ protected internal struct EventData
+ {
+ /// <summary>
+ /// Address where the one argument lives (if this points to managed memory you must ensure the
+ /// managed object is pinned.
+ /// </summary>
+ public IntPtr DataPointer { get { return (IntPtr)m_Ptr; } set { m_Ptr = unchecked((long)value); } }
+ /// <summary>
+ /// Size of the argument referenced by DataPointer
+ /// </summary>
+ public int Size { get { return m_Size; } set { m_Size = value; } }
+
+ #region private
+ /// <summary>
+ /// Initializes the members of this EventData object to point at a previously-pinned
+ /// tracelogging-compatible metadata blob.
+ /// </summary>
+ /// <param name="pointer">Pinned tracelogging-compatible metadata blob.</param>
+ /// <param name="size">The size of the metadata blob.</param>
+ /// <param name="reserved">Value for reserved: 2 for per-provider metadata, 1 for per-event metadata</param>
+ internal unsafe void SetMetadata(byte* pointer, int size, int reserved)
+ {
+ this.m_Ptr = (long)(ulong)(UIntPtr)pointer;
+ this.m_Size = size;
+ this.m_Reserved = reserved; // Mark this descriptor as containing tracelogging-compatible metadata.
+ }
+
+ //Important, we pass this structure directly to the Win32 EventWrite API, so this structure must be layed out exactly
+ // the way EventWrite wants it.
+ internal long m_Ptr;
+ internal int m_Size;
+#pragma warning disable 0649
+ internal int m_Reserved; // Used to pad the size to match the Win32 API
+#pragma warning restore 0649
+ #endregion
+ }
+
+ /// <summary>
+ /// This routine allows you to create efficient WriteEvent helpers, however the code that you use to
+ /// do this, while straightforward, is unsafe.
+ /// </summary>
+ /// <remarks>
+ /// <code>
+ /// protected unsafe void WriteEvent(int eventId, string arg1, long arg2)
+ /// {
+ /// if (IsEnabled())
+ /// {
+ /// if (arg2 == null) arg2 = "";
+ /// fixed (char* string2Bytes = arg2)
+ /// {
+ /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ /// descrs[0].DataPointer = (IntPtr)(&amp;arg1);
+ /// descrs[0].Size = 8;
+ /// descrs[1].DataPointer = (IntPtr)string2Bytes;
+ /// descrs[1].Size = ((arg2.Length + 1) * 2);
+ /// WriteEventCore(eventId, 2, descrs);
+ /// }
+ /// }
+ /// }
+ /// </code>
+ /// </remarks>
+ [CLSCompliant(false)]
+ protected unsafe void WriteEventCore(int eventId, int eventDataCount, EventSource.EventData* data)
+ {
+ WriteEventWithRelatedActivityIdCore(eventId, null, eventDataCount, data);
+ }
+
+ /// <summary>
+ /// This routine allows you to create efficient WriteEventWithRelatedActivityId helpers, however the code
+ /// that you use to do this, while straightforward, is unsafe. The only difference from
+ /// <see cref="WriteEventCore"/> is that you pass the relatedActivityId from caller through to this API
+ /// </summary>
+ /// <remarks>
+ /// <code>
+ /// protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, string arg1, long arg2)
+ /// {
+ /// if (IsEnabled())
+ /// {
+ /// if (arg2 == null) arg2 = "";
+ /// fixed (char* string2Bytes = arg2)
+ /// {
+ /// EventSource.EventData* descrs = stackalloc EventSource.EventData[2];
+ /// descrs[0].DataPointer = (IntPtr)(&amp;arg1);
+ /// descrs[0].Size = 8;
+ /// descrs[1].DataPointer = (IntPtr)string2Bytes;
+ /// descrs[1].Size = ((arg2.Length + 1) * 2);
+ /// WriteEventWithRelatedActivityIdCore(eventId, relatedActivityId, 2, descrs);
+ /// }
+ /// }
+ /// }
+ /// </code>
+ /// </remarks>
+ [CLSCompliant(false)]
+ protected unsafe void WriteEventWithRelatedActivityIdCore(int eventId, Guid* relatedActivityId, int eventDataCount, EventSource.EventData* data)
+ {
+ if (m_eventSourceEnabled)
+ {
+ try
+ {
+ Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
+ if (relatedActivityId != null)
+ ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
+
+ EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
+ EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
+ Guid* pActivityId = null;
+ Guid activityId = Guid.Empty;
+ Guid relActivityId = Guid.Empty;
+
+ if (opcode != EventOpcode.Info && relatedActivityId == null &&
+ ((activityOptions & EventActivityOptions.Disable) == 0))
+ {
+ if (opcode == EventOpcode.Start)
+ {
+ m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relActivityId, m_eventData[eventId].ActivityOptions);
+ }
+ else if (opcode == EventOpcode.Stop)
+ {
+ m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
+ }
+
+ if (activityId != Guid.Empty)
+ pActivityId = &activityId;
+ if (relActivityId != Guid.Empty)
+ relatedActivityId = &relActivityId;
+ }
+
+#if FEATURE_MANAGED_ETW
+ if (m_eventData[eventId].EnabledForETW)
+ {
+
+#if FEATURE_ACTIVITYSAMPLING
+ // this code should be kept in sync with WriteEventVarargs().
+ SessionMask etwSessions = SessionMask.All;
+ // only compute etwSessions if there are *any* ETW filters enabled...
+ if ((ulong)m_curLiveSessions != 0)
+ etwSessions = GetEtwSessionMask(eventId, relatedActivityId);
+ // OutputDebugString(string.Format("{0}.WriteEvent(id {1}) -> to sessions {2:x}",
+ // m_name, m_eventData[eventId].Name, (ulong) etwSessions));
+
+ if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
+ {
+ if (!SelfDescribingEvents)
+ {
+ if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
+ {
+ // OutputDebugString(string.Format(" (1) id {0}, kwd {1:x}",
+ // m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Keywords));
+ // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
+ // mask set to 0x0f so, when all ETW sessions want the event we don't need to
+ // synthesize a new one
+ if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ else
+ {
+ long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
+ // OutputDebugString(string.Format(" (2) id {0}, kwd {1:x}",
+ // m_eventData[eventId].Name, etwSessions.ToEventKeywords() | (ulong) origKwd));
+ // only some of the ETW sessions will receive this event. Synthesize a new
+ // Descriptor whose Keywords field will have the appropriate bits set.
+ // etwSessions might be 0, if there are legacy ETW listeners that want this event
+ var desc = new EventDescriptor(
+ m_eventData[eventId].Descriptor.EventId,
+ m_eventData[eventId].Descriptor.Version,
+ m_eventData[eventId].Descriptor.Channel,
+ m_eventData[eventId].Descriptor.Level,
+ m_eventData[eventId].Descriptor.Opcode,
+ m_eventData[eventId].Descriptor.Task,
+ unchecked((long)etwSessions.ToEventKeywords() | origKwd));
+
+ if (!m_provider.WriteEvent(ref desc, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ }
+ else
+ {
+ TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
+ if (tlet == null)
+ {
+ tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ EventTags.None,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
+
+ }
+ long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
+ // TODO: activity ID support
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
+ Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
+ Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
+ };
+
+ WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
+ }
+ }
+#else
+ if (!SelfDescribingEvents)
+ {
+ if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, relatedActivityId, eventDataCount, (IntPtr)data))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ else
+ {
+ TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
+ if (tlet == null)
+ {
+ tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ m_eventData[eventId].Tags,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
+
+ }
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
+ Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
+ Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
+ };
+
+ WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, relatedActivityId, data);
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+#endif // FEATURE_MANAGED_ETW
+
+ if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
+ WriteToAllListeners(eventId, relatedActivityId, eventDataCount, data);
+ }
+ catch (Exception ex)
+ {
+ if (ex is EventSourceException)
+ throw;
+ else
+ ThrowEventSourceException(m_eventData[eventId].Name, ex);
+ }
+ }
+ }
+
+ // fallback varags helpers.
+ /// <summary>
+ /// This is the varargs helper for writing an event. It does create an array and box all the arguments so it is
+ /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
+ /// rates are faster than that you should use <see cref="WriteEventCore"/> to create fast helpers for your particular
+ /// method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
+ /// check so that the varargs call is not made when the EventSource is not active.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ protected unsafe void WriteEvent(int eventId, params object[] args)
+ {
+ WriteEventVarargs(eventId, null, args);
+ }
+
+ /// <summary>
+ /// This is the varargs helper for writing an event which also specifies a related activity. It is completely analogous
+ /// to corresponding WriteEvent (they share implementation). It does create an array and box all the arguments so it is
+ /// relatively inefficient and should only be used for relatively rare events (e.g. less than 100 / sec). If your
+ /// rates are faster than that you should use <see cref="WriteEventWithRelatedActivityIdCore"/> to create fast helpers for your
+ /// particular method signature. Even if you use this for rare events, this call should be guarded by an <see cref="IsEnabled()"/>
+ /// check so that the varargs call is not made when the EventSource is not active.
+ /// </summary>
+ protected unsafe void WriteEventWithRelatedActivityId(int eventId, Guid relatedActivityId, params object[] args)
+ {
+ WriteEventVarargs(eventId, &relatedActivityId, args);
+ }
+
+ #endregion
+
+ #region IDisposable Members
+ /// <summary>
+ /// Disposes of an EventSource.
+ /// </summary>
+ public void Dispose()
+ {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ /// <summary>
+ /// Disposes of an EventSource.
+ /// </summary>
+ /// <remarks>
+ /// Called from Dispose() with disposing=true, and from the finalizer (~EventSource) with disposing=false.
+ /// Guidelines:
+ /// 1. We may be called more than once: do nothing after the first call.
+ /// 2. Avoid throwing exceptions if disposing is false, i.e. if we're being finalized.
+ /// </remarks>
+ /// <param name="disposing">True if called from Dispose(), false if called from the finalizer.</param>
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+#if FEATURE_MANAGED_ETW
+ // Send the manifest one more time to ensure circular buffers have a chance to get to this information
+ // even in scenarios with a high volume of ETW events.
+ if (m_eventSourceEnabled)
+ {
+ try
+ {
+ SendManifest(m_rawManifest);
+ }
+ catch (Exception)
+ { } // If it fails, simply give up.
+ m_eventSourceEnabled = false;
+ }
+ if (m_provider != null)
+ {
+ m_provider.Dispose();
+ m_provider = null;
+ }
+#endif
+ }
+ m_eventSourceEnabled = false;
+ m_eventSourceDisposed = true;
+ }
+ /// <summary>
+ /// Finalizer for EventSource
+ /// </summary>
+ ~EventSource()
+ {
+ this.Dispose(false);
+ }
+ #endregion
+
+ #region private
+#if FEATURE_ACTIVITYSAMPLING
+ internal void WriteStringToListener(EventListener listener, string msg, SessionMask m)
+ {
+ Debug.Assert(listener == null || (uint)m == (uint)SessionMask.FromId(0));
+
+ if (m_eventSourceEnabled)
+ {
+ if (listener == null)
+ {
+ WriteEventString(0, unchecked((long)m.ToEventKeywords()), msg);
+ }
+ else
+ {
+ EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
+ eventCallbackArgs.EventId = 0;
+ eventCallbackArgs.Message = msg;
+ eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
+ eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
+ eventCallbackArgs.EventName = "EventSourceMessage";
+ listener.OnEventWritten(eventCallbackArgs);
+ }
+ }
+ }
+#endif
+
+ private unsafe void WriteEventRaw(
+ string eventName,
+ ref EventDescriptor eventDescriptor,
+ Guid* activityID,
+ Guid* relatedActivityID,
+ int dataCount,
+ IntPtr data)
+ {
+#if FEATURE_MANAGED_ETW
+ if (m_provider == null)
+ {
+ ThrowEventSourceException(eventName);
+ }
+ else
+ {
+ if (!m_provider.WriteEventRaw(ref eventDescriptor, activityID, relatedActivityID, dataCount, data))
+ ThrowEventSourceException(eventName);
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+
+ // FrameworkEventSource is on the startup path for the framework, so we have this internal overload that it can use
+ // to prevent the working set hit from looking at the custom attributes on the type to get the Guid.
+ internal EventSource(Guid eventSourceGuid, string eventSourceName)
+ : this(eventSourceGuid, eventSourceName, EventSourceSettings.EtwManifestEventFormat)
+ { }
+
+ // Used by the internal FrameworkEventSource constructor and the TraceLogging-style event source constructor
+ internal EventSource(Guid eventSourceGuid, string eventSourceName, EventSourceSettings settings, string[] traits = null)
+ {
+ m_config = ValidateSettings(settings);
+ Initialize(eventSourceGuid, eventSourceName, traits);
+ }
+
+ /// <summary>
+ /// This method is responsible for the common initialization path from our constructors. It must
+ /// not leak any exceptions (otherwise, since most EventSource classes define a static member,
+ /// "Log", such an exception would become a cached exception for the initialization of the static
+ /// member, and any future access to the "Log" would throw the cached exception).
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "guid")]
+ private unsafe void Initialize(Guid eventSourceGuid, string eventSourceName, string[] traits)
+ {
+ try
+ {
+ m_traits = traits;
+ if (m_traits != null && m_traits.Length % 2 != 0)
+ {
+ throw new ArgumentException(Resources.GetResourceString("TraitEven"), nameof(traits));
+ }
+
+ if (eventSourceGuid == Guid.Empty)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_NeedGuid"));
+ }
+
+ if (eventSourceName == null)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_NeedName"));
+ }
+
+ m_name = eventSourceName;
+ m_guid = eventSourceGuid;
+#if FEATURE_ACTIVITYSAMPLING
+ m_curLiveSessions = new SessionMask(0);
+ m_etwSessionIdMap = new EtwSession[SessionMask.MAX];
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ //Enable Implicit Activity tracker
+ m_activityTracker = ActivityTracker.Instance;
+
+#if FEATURE_MANAGED_ETW
+ // Create and register our provider traits. We do this early because it is needed to log errors
+ // In the self-describing event case.
+ this.InitializeProviderMetadata();
+
+ // Register the provider with ETW
+ var provider = new OverideEventProvider(this);
+ provider.Register(eventSourceGuid);
+#endif
+ // Add the eventSource to the global (weak) list.
+ // This also sets m_id, which is the index in the list.
+ EventListener.AddEventSource(this);
+
+#if FEATURE_MANAGED_ETW
+ // OK if we get this far without an exception, then we can at least write out error messages.
+ // Set m_provider, which allows this.
+ m_provider = provider;
+
+#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
+ // API available on OS >= Win 8 and patched Win 7.
+ // Disable only for FrameworkEventSource to avoid recursion inside exception handling.
+ if (this.Name != "System.Diagnostics.Eventing.FrameworkEventSource" || Environment.IsWindows8OrAbove)
+#endif
+ {
+ int setInformationResult;
+ System.Runtime.InteropServices.GCHandle metadataHandle =
+ System.Runtime.InteropServices.GCHandle.Alloc(this.providerMetadata, System.Runtime.InteropServices.GCHandleType.Pinned);
+ IntPtr providerMetadata = metadataHandle.AddrOfPinnedObject();
+
+ setInformationResult = m_provider.SetInformation(
+ UnsafeNativeMethods.ManifestEtw.EVENT_INFO_CLASS.SetTraits,
+ providerMetadata,
+ (uint)this.providerMetadata.Length);
+
+ metadataHandle.Free();
+ }
+#endif // FEATURE_MANAGED_ETW
+
+ Debug.Assert(!m_eventSourceEnabled); // We can't be enabled until we are completely initted.
+ // We are logically completely initialized at this point.
+ m_completelyInited = true;
+ }
+ catch (Exception e)
+ {
+ if (m_constructionException == null)
+ m_constructionException = e;
+ ReportOutOfBandMessage("ERROR: Exception during construction of EventSource " + Name + ": " + e.Message, true);
+ }
+
+ // Once m_completelyInited is set, you can have concurrency, so all work is under the lock.
+ lock (EventListener.EventListenersLock)
+ {
+ // If there are any deferred commands, we can do them now.
+ // This is the most likely place for exceptions to happen.
+ // Note that we are NOT resetting m_deferredCommands to NULL here,
+ // We are giving for EventHandler<EventCommandEventArgs> that will be attached later
+ EventCommandEventArgs deferredCommands = m_deferredCommands;
+ while (deferredCommands != null)
+ {
+ DoCommand(deferredCommands); // This can never throw, it catches them and reports the errors.
+ deferredCommands = deferredCommands.nextCommand;
+ }
+ }
+ }
+
+ private static string GetName(Type eventSourceType, EventManifestOptions flags)
+ {
+ if (eventSourceType == null)
+ throw new ArgumentNullException(nameof(eventSourceType));
+ Contract.EndContractBlock();
+
+ EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
+ if (attrib != null && attrib.Name != null)
+ return attrib.Name;
+
+ return eventSourceType.Name;
+ }
+
+ /// <summary>
+ /// Implements the SHA1 hashing algorithm. Note that this
+ /// implementation is for hashing public information. Do not
+ /// use this code to hash private data, as this implementation does
+ /// not take any steps to avoid information disclosure.
+ /// </summary>
+ private struct Sha1ForNonSecretPurposes
+ {
+ private long length; // Total message length in bits
+ private uint[] w; // Workspace
+ private int pos; // Length of current chunk in bytes
+
+ /// <summary>
+ /// Call Start() to initialize the hash object.
+ /// </summary>
+ public void Start()
+ {
+ if (this.w == null)
+ {
+ this.w = new uint[85];
+ }
+
+ this.length = 0;
+ this.pos = 0;
+ this.w[80] = 0x67452301;
+ this.w[81] = 0xEFCDAB89;
+ this.w[82] = 0x98BADCFE;
+ this.w[83] = 0x10325476;
+ this.w[84] = 0xC3D2E1F0;
+ }
+
+ /// <summary>
+ /// Adds an input byte to the hash.
+ /// </summary>
+ /// <param name="input">Data to include in the hash.</param>
+ public void Append(byte input)
+ {
+ this.w[this.pos / 4] = (this.w[this.pos / 4] << 8) | input;
+ if (64 == ++this.pos)
+ {
+ this.Drain();
+ }
+ }
+
+ /// <summary>
+ /// Adds input bytes to the hash.
+ /// </summary>
+ /// <param name="input">
+ /// Data to include in the hash. Must not be null.
+ /// </param>
+ public void Append(byte[] input)
+ {
+ foreach (var b in input)
+ {
+ this.Append(b);
+ }
+ }
+
+ /// <summary>
+ /// Retrieves the hash value.
+ /// Note that after calling this function, the hash object should
+ /// be considered uninitialized. Subsequent calls to Append or
+ /// Finish will produce useless results. Call Start() to
+ /// reinitialize.
+ /// </summary>
+ /// <param name="output">
+ /// Buffer to receive the hash value. Must not be null.
+ /// Up to 20 bytes of hash will be written to the output buffer.
+ /// If the buffer is smaller than 20 bytes, the remaining hash
+ /// bytes will be lost. If the buffer is larger than 20 bytes, the
+ /// rest of the buffer is left unmodified.
+ /// </param>
+ public void Finish(byte[] output)
+ {
+ long l = this.length + 8 * this.pos;
+ this.Append(0x80);
+ while (this.pos != 56)
+ {
+ this.Append(0x00);
+ }
+
+ unchecked
+ {
+ this.Append((byte)(l >> 56));
+ this.Append((byte)(l >> 48));
+ this.Append((byte)(l >> 40));
+ this.Append((byte)(l >> 32));
+ this.Append((byte)(l >> 24));
+ this.Append((byte)(l >> 16));
+ this.Append((byte)(l >> 8));
+ this.Append((byte)l);
+
+ int end = output.Length < 20 ? output.Length : 20;
+ for (int i = 0; i != end; i++)
+ {
+ uint temp = this.w[80 + i / 4];
+ output[i] = (byte)(temp >> 24);
+ this.w[80 + i / 4] = temp << 8;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Called when this.pos reaches 64.
+ /// </summary>
+ private void Drain()
+ {
+ for (int i = 16; i != 80; i++)
+ {
+ this.w[i] = Rol1((this.w[i - 3] ^ this.w[i - 8] ^ this.w[i - 14] ^ this.w[i - 16]));
+ }
+
+ unchecked
+ {
+ uint a = this.w[80];
+ uint b = this.w[81];
+ uint c = this.w[82];
+ uint d = this.w[83];
+ uint e = this.w[84];
+
+ for (int i = 0; i != 20; i++)
+ {
+ const uint k = 0x5A827999;
+ uint f = (b & c) | ((~b) & d);
+ uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
+ }
+
+ for (int i = 20; i != 40; i++)
+ {
+ uint f = b ^ c ^ d;
+ const uint k = 0x6ED9EBA1;
+ uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
+ }
+
+ for (int i = 40; i != 60; i++)
+ {
+ uint f = (b & c) | (b & d) | (c & d);
+ const uint k = 0x8F1BBCDC;
+ uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
+ }
+
+ for (int i = 60; i != 80; i++)
+ {
+ uint f = b ^ c ^ d;
+ const uint k = 0xCA62C1D6;
+ uint temp = Rol5(a) + f + e + k + this.w[i]; e = d; d = c; c = Rol30(b); b = a; a = temp;
+ }
+
+ this.w[80] += a;
+ this.w[81] += b;
+ this.w[82] += c;
+ this.w[83] += d;
+ this.w[84] += e;
+ }
+
+ this.length += 512; // 64 bytes == 512 bits
+ this.pos = 0;
+ }
+
+ private static uint Rol1(uint input)
+ {
+ return (input << 1) | (input >> 31);
+ }
+
+ private static uint Rol5(uint input)
+ {
+ return (input << 5) | (input >> 27);
+ }
+
+ private static uint Rol30(uint input)
+ {
+ return (input << 30) | (input >> 2);
+ }
+ }
+
+ private static Guid GenerateGuidFromName(string name)
+ {
+ byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
+ var hash = new Sha1ForNonSecretPurposes();
+ hash.Start();
+ hash.Append(namespaceBytes);
+ hash.Append(bytes);
+ Array.Resize(ref bytes, 16);
+ hash.Finish(bytes);
+
+ bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
+ return new Guid(bytes);
+ }
+
+ private unsafe object DecodeObject(int eventId, int parameterId, ref EventSource.EventData* data)
+ {
+ // TODO FIX : We use reflection which in turn uses EventSource, right now we carefully avoid
+ // the recursion, but can we do this in a robust way?
+
+ IntPtr dataPointer = data->DataPointer;
+ // advance to next EventData in array
+ ++data;
+
+ Type dataType = GetDataType(m_eventData[eventId], parameterId);
+
+ Again:
+ if (dataType == typeof(IntPtr))
+ {
+ return *((IntPtr*)dataPointer);
+ }
+ else if (dataType == typeof(int))
+ {
+ return *((int*)dataPointer);
+ }
+ else if (dataType == typeof(uint))
+ {
+ return *((uint*)dataPointer);
+ }
+ else if (dataType == typeof(long))
+ {
+ return *((long*)dataPointer);
+ }
+ else if (dataType == typeof(ulong))
+ {
+ return *((ulong*)dataPointer);
+ }
+ else if (dataType == typeof(byte))
+ {
+ return *((byte*)dataPointer);
+ }
+ else if (dataType == typeof(sbyte))
+ {
+ return *((sbyte*)dataPointer);
+ }
+ else if (dataType == typeof(short))
+ {
+ return *((short*)dataPointer);
+ }
+ else if (dataType == typeof(ushort))
+ {
+ return *((ushort*)dataPointer);
+ }
+ else if (dataType == typeof(float))
+ {
+ return *((float*)dataPointer);
+ }
+ else if (dataType == typeof(double))
+ {
+ return *((double*)dataPointer);
+ }
+ else if (dataType == typeof(decimal))
+ {
+ return *((decimal*)dataPointer);
+ }
+ else if (dataType == typeof(bool))
+ {
+ // The manifest defines a bool as a 32bit type (WIN32 BOOL), not 1 bit as CLR Does.
+ if (*((int*)dataPointer) == 1)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else if (dataType == typeof(Guid))
+ {
+ return *((Guid*)dataPointer);
+ }
+ else if (dataType == typeof(char))
+ {
+ return *((char*)dataPointer);
+ }
+ else if (dataType == typeof(DateTime))
+ {
+ long dateTimeTicks = *((long*)dataPointer);
+ return DateTime.FromFileTimeUtc(dateTimeTicks);
+ }
+ else if (dataType == typeof(byte[]))
+ {
+ // byte[] are written to EventData* as an int followed by a blob
+ int cbSize = *((int*)dataPointer);
+ byte[] blob = new byte[cbSize];
+ dataPointer = data->DataPointer;
+ data++;
+ for (int i = 0; i < cbSize; ++i)
+ blob[i] = *((byte*)(dataPointer + i));
+ return blob;
+ }
+ else if (dataType == typeof(byte*))
+ {
+ // TODO: how do we want to handle this? For now we ignore it...
+ return null;
+ }
+ else
+ {
+ if (m_EventSourcePreventRecursion && m_EventSourceInDecodeObject)
+ {
+ return null;
+ }
+
+ try
+ {
+ m_EventSourceInDecodeObject = true;
+
+ if (dataType.IsEnum())
+ {
+ dataType = Enum.GetUnderlyingType(dataType);
+ goto Again;
+ }
+
+
+ // Everything else is marshaled as a string.
+ // ETW strings are NULL-terminated, so marshal everything up to the first
+ // null in the string.
+ //return System.Runtime.InteropServices.Marshal.PtrToStringUni(dataPointer);
+ if(dataPointer == IntPtr.Zero)
+ {
+ return null;
+ }
+
+ return new string((char *)dataPointer);
+
+ }
+ finally
+ {
+ m_EventSourceInDecodeObject = false;
+ }
+ }
+ }
+
+ // Finds the Dispatcher (which holds the filtering state), for a given dispatcher for the current
+ // eventSource).
+ private EventDispatcher GetDispatcher(EventListener listener)
+ {
+ EventDispatcher dispatcher = m_Dispatchers;
+ while (dispatcher != null)
+ {
+ if (dispatcher.m_Listener == listener)
+ return dispatcher;
+ dispatcher = dispatcher.m_Next;
+ }
+ return dispatcher;
+ }
+
+ private unsafe void WriteEventVarargs(int eventId, Guid* childActivityID, object[] args)
+ {
+ if (m_eventSourceEnabled)
+ {
+ try
+ {
+ Debug.Assert(m_eventData != null); // You must have initialized this if you enabled the source.
+ if (childActivityID != null)
+ {
+ ValidateEventOpcodeForTransfer(ref m_eventData[eventId], m_eventData[eventId].Name);
+
+ // If you use WriteEventWithRelatedActivityID you MUST declare the first argument to be a GUID
+ // with the name 'relatedActivityID, and NOT pass this argument to the WriteEvent method.
+ // During manifest creation we modify the ParameterInfo[] that we store to strip out any
+ // first parameter that is of type Guid and named "relatedActivityId." Thus, if you call
+ // WriteEventWithRelatedActivityID from a method that doesn't name its first parameter correctly
+ // we can end up in a state where the ParameterInfo[] doesn't have its first parameter stripped,
+ // and this leads to a mismatch between the number of arguments and the number of ParameterInfos,
+ // which would cause a cryptic IndexOutOfRangeException later if we don't catch it here.
+ if (!m_eventData[eventId].HasRelatedActivityID)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_NoRelatedActivityId"));
+ }
+ }
+
+ LogEventArgsMismatches(m_eventData[eventId].Parameters, args);
+
+ Guid* pActivityId = null;
+ Guid activityId = Guid.Empty;
+ Guid relatedActivityId = Guid.Empty;
+ EventOpcode opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode;
+ EventActivityOptions activityOptions = m_eventData[eventId].ActivityOptions;
+
+ if (childActivityID == null &&
+ ((activityOptions & EventActivityOptions.Disable) == 0))
+ {
+ if (opcode == EventOpcode.Start)
+ {
+ m_activityTracker.OnStart(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId, ref relatedActivityId, m_eventData[eventId].ActivityOptions);
+ }
+ else if (opcode == EventOpcode.Stop)
+ {
+ m_activityTracker.OnStop(m_name, m_eventData[eventId].Name, m_eventData[eventId].Descriptor.Task, ref activityId);
+ }
+
+ if (activityId != Guid.Empty)
+ pActivityId = &activityId;
+ if (relatedActivityId != Guid.Empty)
+ childActivityID = &relatedActivityId;
+ }
+
+#if FEATURE_MANAGED_ETW
+ if (m_eventData[eventId].EnabledForETW)
+ {
+#if FEATURE_ACTIVITYSAMPLING
+ // this code should be kept in sync with WriteEventWithRelatedActivityIdCore().
+ SessionMask etwSessions = SessionMask.All;
+ // only compute etwSessions if there are *any* ETW filters enabled...
+ if ((ulong)m_curLiveSessions != 0)
+ etwSessions = GetEtwSessionMask(eventId, childActivityID);
+
+ if ((ulong)etwSessions != 0 || m_legacySessions != null && m_legacySessions.Count > 0)
+ {
+ if (!SelfDescribingEvents)
+ {
+ if (etwSessions.IsEqualOrSupersetOf(m_curLiveSessions))
+ {
+ // by default the Descriptor.Keyword will have the perEventSourceSessionId bit
+ // mask set to 0x0f so, when all ETW sessions want the event we don't need to
+ // synthesize a new one
+ if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ else
+ {
+ long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
+ // only some of the ETW sessions will receive this event. Synthesize a new
+ // Descriptor whose Keywords field will have the appropriate bits set.
+ var desc = new EventDescriptor(
+ m_eventData[eventId].Descriptor.EventId,
+ m_eventData[eventId].Descriptor.Version,
+ m_eventData[eventId].Descriptor.Channel,
+ m_eventData[eventId].Descriptor.Level,
+ m_eventData[eventId].Descriptor.Opcode,
+ m_eventData[eventId].Descriptor.Task,
+ unchecked((long)etwSessions.ToEventKeywords() | origKwd));
+
+ if (!m_provider.WriteEvent(ref desc, pActivityId, childActivityID, args))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ }
+ else
+ {
+ TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
+ if (tlet == null)
+ {
+ tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ EventTags.None,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
+
+ }
+ long origKwd = unchecked((long)((ulong)m_eventData[eventId].Descriptor.Keywords & ~(SessionMask.All.ToEventKeywords())));
+ // TODO: activity ID support
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)unchecked((long)etwSessions.ToEventKeywords() | origKwd),
+ Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
+ Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
+ };
+
+ WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
+ }
+ }
+#else
+ if (!SelfDescribingEvents)
+ {
+ if (!m_provider.WriteEvent(ref m_eventData[eventId].Descriptor, pActivityId, childActivityID, args))
+ ThrowEventSourceException(m_eventData[eventId].Name);
+ }
+ else
+ {
+ TraceLoggingEventTypes tlet = m_eventData[eventId].TraceLoggingEventTypes;
+ if (tlet == null)
+ {
+ tlet = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ EventTags.None,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, tlet, null);
+
+ }
+ // TODO: activity ID support
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)m_eventData[eventId].Descriptor.Keywords,
+ Level = (EventLevel)m_eventData[eventId].Descriptor.Level,
+ Opcode = (EventOpcode)m_eventData[eventId].Descriptor.Opcode
+ };
+
+ WriteMultiMerge(m_eventData[eventId].Name, ref opt, tlet, pActivityId, childActivityID, args);
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+#endif // FEATURE_MANAGED_ETW
+ if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
+ {
+#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
+ // Maintain old behavior - object identity is preserved
+ if (AppContextSwitches.PreserveEventListnerObjectIdentity)
+ {
+ WriteToAllListeners(eventId, childActivityID, args);
+ }
+ else
+#endif // !ES_BUILD_STANDALONE
+ {
+ object[] serializedArgs = SerializeEventArgs(eventId, args);
+ WriteToAllListeners(eventId, childActivityID, serializedArgs);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ if (ex is EventSourceException)
+ throw;
+ else
+ ThrowEventSourceException(m_eventData[eventId].Name, ex);
+ }
+ }
+ }
+
+ unsafe private object[] SerializeEventArgs(int eventId, object[] args)
+ {
+ TraceLoggingEventTypes eventTypes = m_eventData[eventId].TraceLoggingEventTypes;
+ if (eventTypes == null)
+ {
+ eventTypes = new TraceLoggingEventTypes(m_eventData[eventId].Name,
+ EventTags.None,
+ m_eventData[eventId].Parameters);
+ Interlocked.CompareExchange(ref m_eventData[eventId].TraceLoggingEventTypes, eventTypes, null);
+ }
+ var eventData = new object[eventTypes.typeInfos.Length];
+ for (int i = 0; i < eventTypes.typeInfos.Length; i++)
+ {
+ eventData[i] = eventTypes.typeInfos[i].GetData(args[i]);
+ }
+ return eventData;
+ }
+
+ /// <summary>
+ /// We expect that the arguments to the Event method and the arguments to WriteEvent match. This function
+ /// checks that they in fact match and logs a warning to the debugger if they don't.
+ /// </summary>
+ /// <param name="infos"></param>
+ /// <param name="args"></param>
+ private void LogEventArgsMismatches(ParameterInfo[] infos, object[] args)
+ {
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+ // It would be nice to have this on PCL builds, but it would be pointless since there isn't support for
+ // writing to the debugger log on PCL.
+ bool typesMatch = args.Length == infos.Length;
+
+ int i = 0;
+ while (typesMatch && i < args.Length)
+ {
+ Type pType = infos[i].ParameterType;
+
+ // Checking to see if the Parameter types (from the Event method) match the supplied argument types.
+ // Fail if one of two things hold : either the argument type is not equal to the parameter type, or the
+ // argument is null and the parameter type is non-nullable.
+ if ((args[i] != null && (args[i].GetType() != pType))
+ || (args[i] == null && (!(pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))))
+ )
+ {
+ typesMatch = false;
+ break;
+ }
+
+ ++i;
+ }
+
+ if (!typesMatch)
+ {
+ System.Diagnostics.Debugger.Log(0, null, Resources.GetResourceString("EventSource_VarArgsParameterMismatch") + "\r\n");
+ }
+#endif //!ES_BUILD_PCL
+ }
+
+ private int GetParamLenghtIncludingByteArray(ParameterInfo[] parameters)
+ {
+ int sum = 0;
+ foreach (ParameterInfo info in parameters)
+ {
+ if (info.ParameterType == typeof(byte[]))
+ {
+ sum += 2;
+ }
+ else
+ {
+ sum++;
+ }
+ }
+
+ return sum;
+ }
+
+ unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, int eventDataCount, EventSource.EventData* data)
+ {
+ // We represent a byte[] as a integer denoting the length and then a blob of bytes in the data pointer. This causes a spurious
+ // warning because eventDataCount is off by one for the byte[] case since a byte[] has 2 items associated it. So we want to check
+ // that the number of parameters is correct against the byte[] case, but also we the args array would be one too long if
+ // we just used the modifiedParamCount here -- so we need both.
+ int paramCount = m_eventData[eventId].Parameters.Length;
+ int modifiedParamCount = GetParamLenghtIncludingByteArray(m_eventData[eventId].Parameters);
+ if (eventDataCount != modifiedParamCount)
+ {
+ ReportOutOfBandMessage(Resources.GetResourceString("EventSource_EventParametersMismatch", eventId, eventDataCount, paramCount), true);
+ paramCount = Math.Min(paramCount, eventDataCount);
+ }
+
+ object[] args = new object[paramCount];
+
+ EventSource.EventData* dataPtr = data;
+ for (int i = 0; i < paramCount; i++)
+ args[i] = DecodeObject(eventId, i, ref dataPtr);
+ WriteToAllListeners(eventId, childActivityID, args);
+ }
+
+ // helper for writing to all EventListeners attached the current eventSource.
+ unsafe private void WriteToAllListeners(int eventId, Guid* childActivityID, params object[] args)
+ {
+ EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
+ eventCallbackArgs.EventId = eventId;
+ if (childActivityID != null)
+ eventCallbackArgs.RelatedActivityId = *childActivityID;
+ eventCallbackArgs.EventName = m_eventData[eventId].Name;
+ eventCallbackArgs.Message = m_eventData[eventId].Message;
+ eventCallbackArgs.Payload = new ReadOnlyCollection<object>(args);
+
+ DispatchToAllListeners(eventId, childActivityID, eventCallbackArgs);
+ }
+
+ private unsafe void DispatchToAllListeners(int eventId, Guid* childActivityID, EventWrittenEventArgs eventCallbackArgs)
+ {
+ Exception lastThrownException = null;
+ for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
+ {
+ Debug.Assert(dispatcher.m_EventEnabled != null);
+ if (eventId == -1 || dispatcher.m_EventEnabled[eventId])
+ {
+#if FEATURE_ACTIVITYSAMPLING
+ var activityFilter = dispatcher.m_Listener.m_activityFilter;
+ // order below is important as PassesActivityFilter will "flow" active activities
+ // even when the current EventSource doesn't have filtering enabled. This allows
+ // interesting activities to be updated so that sources that do sample can get
+ // accurate data
+ if (activityFilter == null ||
+ ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
+ m_eventData[eventId].TriggersActivityTracking > 0,
+ this, eventId) ||
+ !dispatcher.m_activityFilteringEnabled)
+#endif // FEATURE_ACTIVITYSAMPLING
+ {
+ try
+ {
+ dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
+ }
+ catch (Exception e)
+ {
+ ReportOutOfBandMessage("ERROR: Exception during EventSource.OnEventWritten: "
+ + e.Message, false);
+ lastThrownException = e;
+ }
+ }
+ }
+ }
+
+ if (lastThrownException != null)
+ {
+ throw new EventSourceException(lastThrownException);
+ }
+ }
+
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ private unsafe void WriteEventString(EventLevel level, long keywords, string msgString)
+ {
+#if FEATURE_MANAGED_ETW
+ if (m_provider != null)
+ {
+ string eventName = "EventSourceMessage";
+ if (SelfDescribingEvents)
+ {
+ EventSourceOptions opt = new EventSourceOptions
+ {
+ Keywords = (EventKeywords)unchecked(keywords),
+ Level = level
+ };
+ var msg = new { message = msgString };
+ var tlet = new TraceLoggingEventTypes(eventName, EventTags.None, new Type[] { msg.GetType() });
+ WriteMultiMergeInner(eventName, ref opt, tlet, null, null, msg);
+ }
+ else
+ {
+ // We want the name of the provider to show up so if we don't have a manifest we create
+ // on that at least has the provider name (I don't define any events).
+ if (m_rawManifest == null && m_outOfBandMessageCount == 1)
+ {
+ ManifestBuilder manifestBuilder = new ManifestBuilder(Name, Guid, Name, null, EventManifestOptions.None);
+ manifestBuilder.StartEvent(eventName, new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
+ manifestBuilder.AddEventParameter(typeof(string), "message");
+ manifestBuilder.EndEvent();
+ SendManifest(manifestBuilder.CreateManifest());
+ }
+
+ // We use this low level routine to to bypass the enabled checking, since the eventSource itself is only partially inited.
+ fixed (char* msgStringPtr = msgString)
+ {
+ EventDescriptor descr = new EventDescriptor(0, 0, 0, (byte)level, 0, 0, keywords);
+ EventProvider.EventData data = new EventProvider.EventData();
+ data.Ptr = (ulong)msgStringPtr;
+ data.Size = (uint)(2 * (msgString.Length + 1));
+ data.Reserved = 0;
+ m_provider.WriteEvent(ref descr, null, null, 1, (IntPtr)((void*)&data));
+ }
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+
+ /// <summary>
+ /// Since this is a means of reporting errors (see ReportoutOfBandMessage) any failure encountered
+ /// while writing the message to any one of the listeners will be silently ignored.
+ /// </summary>
+ private void WriteStringToAllListeners(string eventName, string msg)
+ {
+ EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
+ eventCallbackArgs.EventId = 0;
+ eventCallbackArgs.Message = msg;
+ eventCallbackArgs.Payload = new ReadOnlyCollection<object>(new List<object>() { msg });
+ eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>(new List<string> { "message" });
+ eventCallbackArgs.EventName = eventName;
+
+ for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
+ {
+ bool dispatcherEnabled = false;
+ if (dispatcher.m_EventEnabled == null)
+ {
+ // if the listeners that weren't correctly initialized, we will send to it
+ // since this is an error message and we want to see it go out.
+ dispatcherEnabled = true;
+ }
+ else
+ {
+ // if there's *any* enabled event on the dispatcher we'll write out the string
+ // otherwise we'll treat the listener as disabled and skip it
+ for (int evtId = 0; evtId < dispatcher.m_EventEnabled.Length; ++evtId)
+ {
+ if (dispatcher.m_EventEnabled[evtId])
+ {
+ dispatcherEnabled = true;
+ break;
+ }
+ }
+ }
+ try
+ {
+ if (dispatcherEnabled)
+ dispatcher.m_Listener.OnEventWritten(eventCallbackArgs);
+ }
+ catch
+ {
+ // ignore any exceptions thrown by listeners' OnEventWritten
+ }
+ }
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ unsafe private SessionMask GetEtwSessionMask(int eventId, Guid* childActivityID)
+ {
+ SessionMask etwSessions = new SessionMask();
+
+ for (int i = 0; i < SessionMask.MAX; ++i)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[i];
+ if (etwSession != null)
+ {
+ ActivityFilter activityFilter = etwSession.m_activityFilter;
+ // PassesActivityFilter() will flow "interesting" activities, so make sure
+ // to perform this test first, before ORing with ~m_activityFilteringForETWEnabled
+ // (note: the first test for !m_activityFilteringForETWEnabled[i] ensures we
+ // do not fire events indiscriminately, when no filters are specified, but only
+ // if, in addition, the session did not also enable ActivitySampling)
+ if (activityFilter == null && !m_activityFilteringForETWEnabled[i] ||
+ activityFilter != null &&
+ ActivityFilter.PassesActivityFilter(activityFilter, childActivityID,
+ m_eventData[eventId].TriggersActivityTracking > 0, this, eventId) ||
+ !m_activityFilteringForETWEnabled[i])
+ {
+ etwSessions[i] = true;
+ }
+ }
+ }
+ // flow "interesting" activities for all legacy sessions in which there's some
+ // level of activity tracing enabled (even other EventSources)
+ if (m_legacySessions != null && m_legacySessions.Count > 0 &&
+ (EventOpcode)m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send)
+ {
+ // only calculate InternalCurrentThreadActivityId once
+ Guid* pCurrentActivityId = null;
+ Guid currentActivityId;
+ foreach (var legacyEtwSession in m_legacySessions)
+ {
+ if (legacyEtwSession == null)
+ continue;
+
+ ActivityFilter activityFilter = legacyEtwSession.m_activityFilter;
+ if (activityFilter != null)
+ {
+ if (pCurrentActivityId == null)
+ {
+ currentActivityId = InternalCurrentThreadActivityId;
+ pCurrentActivityId = &currentActivityId;
+ }
+ ActivityFilter.FlowActivityIfNeeded(activityFilter, pCurrentActivityId, childActivityID);
+ }
+ }
+ }
+
+ return etwSessions;
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ /// <summary>
+ /// Returns true if 'eventNum' is enabled if you only consider the level and matchAnyKeyword filters.
+ /// It is possible that eventSources turn off the event based on additional filtering criteria.
+ /// </summary>
+ private bool IsEnabledByDefault(int eventNum, bool enable, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword)
+ {
+ if (!enable)
+ return false;
+
+ EventLevel eventLevel = (EventLevel)m_eventData[eventNum].Descriptor.Level;
+ EventKeywords eventKeywords = unchecked((EventKeywords)((ulong)m_eventData[eventNum].Descriptor.Keywords & (~(SessionMask.All.ToEventKeywords()))));
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ EventChannel channel = unchecked((EventChannel)m_eventData[eventNum].Descriptor.Channel);
+#else
+ EventChannel channel = EventChannel.None;
+#endif
+
+ return IsEnabledCommon(enable, currentLevel, currentMatchAnyKeyword, eventLevel, eventKeywords, channel);
+ }
+
+ private bool IsEnabledCommon(bool enabled, EventLevel currentLevel, EventKeywords currentMatchAnyKeyword,
+ EventLevel eventLevel, EventKeywords eventKeywords, EventChannel eventChannel)
+ {
+ if (!enabled)
+ return false;
+
+ // does is pass the level test?
+ if ((currentLevel != 0) && (currentLevel < eventLevel))
+ return false;
+
+ // if yes, does it pass the keywords test?
+ if (currentMatchAnyKeyword != 0 && eventKeywords != 0)
+ {
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // is there a channel with keywords that match currentMatchAnyKeyword?
+ if (eventChannel != EventChannel.None && this.m_channelData != null && this.m_channelData.Length > (int)eventChannel)
+ {
+ EventKeywords channel_keywords = unchecked((EventKeywords)(m_channelData[(int)eventChannel] | (ulong)eventKeywords));
+ if (channel_keywords != 0 && (channel_keywords & currentMatchAnyKeyword) == 0)
+ return false;
+ }
+ else
+#endif
+ {
+ if ((unchecked((ulong)eventKeywords & (ulong)currentMatchAnyKeyword)) == 0)
+ return false;
+ }
+ }
+ return true;
+
+ }
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
+ private void ThrowEventSourceException(string eventName, Exception innerEx = null)
+ {
+ // If we fail during out of band logging we may end up trying
+ // to throw another EventSourceException, thus hitting a StackOverflowException.
+ // Avoid StackOverflow by making sure we do not recursively call this method.
+ if (m_EventSourceExceptionRecurenceCount > 0)
+ return;
+ try
+ {
+ m_EventSourceExceptionRecurenceCount++;
+
+ string errorPrefix = "EventSourceException";
+ if (eventName != null)
+ {
+ errorPrefix += " while processing event \"" + eventName + "\"";
+ }
+
+ // TODO Create variations of EventSourceException that indicate more information using the error code.
+ switch (EventProvider.GetLastWriteEventError())
+ {
+ case EventProvider.WriteEventErrorCode.EventTooBig:
+ ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_EventTooBig"), true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_EventTooBig"), innerEx);
+ break;
+ case EventProvider.WriteEventErrorCode.NoFreeBuffers:
+ ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_NoFreeBuffers"), true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_NoFreeBuffers"), innerEx);
+ break;
+ case EventProvider.WriteEventErrorCode.NullInput:
+ ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_NullInput"), true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_NullInput"), innerEx);
+ break;
+ case EventProvider.WriteEventErrorCode.TooManyArgs:
+ ReportOutOfBandMessage(errorPrefix + ": " + Resources.GetResourceString("EventSource_TooManyArgs"), true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(Resources.GetResourceString("EventSource_TooManyArgs"), innerEx);
+ break;
+ default:
+ if (innerEx != null)
+ ReportOutOfBandMessage(errorPrefix + ": " + innerEx.GetType() + ":" + innerEx.Message, true);
+ else
+ ReportOutOfBandMessage(errorPrefix, true);
+ if (ThrowOnEventWriteErrors) throw new EventSourceException(innerEx);
+ break;
+ }
+ }
+ finally
+ {
+ m_EventSourceExceptionRecurenceCount--;
+ }
+ }
+
+ private void ValidateEventOpcodeForTransfer(ref EventMetadata eventData, string eventName)
+ {
+ if ((EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Send &&
+ (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Receive &&
+ (EventOpcode)eventData.Descriptor.Opcode != EventOpcode.Start)
+ {
+ ThrowEventSourceException(eventName);
+ }
+ }
+
+ internal static EventOpcode GetOpcodeWithDefault(EventOpcode opcode, string eventName)
+ {
+ if (opcode == EventOpcode.Info && eventName != null)
+ {
+ if (eventName.EndsWith(s_ActivityStartSuffix, StringComparison.Ordinal))
+ {
+ return EventOpcode.Start;
+ }
+ else if (eventName.EndsWith(s_ActivityStopSuffix, StringComparison.Ordinal))
+ {
+ return EventOpcode.Stop;
+ }
+ }
+
+ return opcode;
+ }
+
+#if FEATURE_MANAGED_ETW
+ /// <summary>
+ /// This class lets us hook the 'OnEventCommand' from the eventSource.
+ /// </summary>
+ private class OverideEventProvider : EventProvider
+ {
+ public OverideEventProvider(EventSource eventSource)
+ {
+ this.m_eventSource = eventSource;
+ }
+ protected override void OnControllerCommand(ControllerCommand command, IDictionary<string, string> arguments,
+ int perEventSourceSessionId, int etwSessionId)
+ {
+ // We use null to represent the ETW EventListener.
+ EventListener listener = null;
+ m_eventSource.SendCommand(listener, perEventSourceSessionId, etwSessionId,
+ (EventCommand)command, IsEnabled(), Level, MatchAnyKeyword, arguments);
+ }
+ private EventSource m_eventSource;
+ }
+#endif
+
+ /// <summary>
+ /// Used to hold all the static information about an event. This includes everything in the event
+ /// descriptor as well as some stuff we added specifically for EventSource. see the
+ /// code:m_eventData for where we use this.
+ /// </summary>
+
+ /*
+ EventMetadata was public in the separate System.Diagnostics.Tracing assembly(pre NS2.0),
+ now the move to CoreLib marked them as private.
+ While they are technically private (it's a contract used between the library and the ILC toolchain),
+ we need them to be rooted and exported from shared library for the system to work.
+ For now I'm simply marking them as public again.A cleaner solution might be to use.rd.xml to
+ root them and modify shared library definition to force export them.
+ */
+#if ES_BUILD_PN
+ public
+#else
+ internal
+#endif
+ partial struct EventMetadata
+ {
+ public EventDescriptor Descriptor;
+ public EventTags Tags;
+ public bool EnabledForAnyListener; // true if any dispatcher has this event turned on
+ public bool EnabledForETW; // is this event on for the OS ETW data dispatcher?
+
+ public bool HasRelatedActivityID; // Set if the event method's first parameter is a Guid named 'relatedActivityId'
+#if !FEATURE_ACTIVITYSAMPLING
+#pragma warning disable 0649
+#endif
+ public byte TriggersActivityTracking; // count of listeners that marked this event as trigger for start of activity logging.
+#if !FEATURE_ACTIVITYSAMPLING
+#pragma warning restore 0649
+#endif
+ public string Name; // the name of the event
+ public string Message; // If the event has a message associated with it, this is it.
+ public ParameterInfo[] Parameters; // TODO can we remove?
+
+ public TraceLoggingEventTypes TraceLoggingEventTypes;
+ public EventActivityOptions ActivityOptions;
+
+#if ES_BUILD_PN
+ public EventParameterType[] ParameterTypes;
+#endif
+ };
+
+ // This is the internal entry point that code:EventListeners call when wanting to send a command to a
+ // eventSource. The logic is as follows
+ //
+ // * if Command == Update
+ // * perEventSourceSessionId specifies the per-provider ETW session ID that the command applies
+ // to (if listener != null)
+ // perEventSourceSessionId = 0 - reserved for EventListeners
+ // perEventSourceSessionId = 1..SessionMask.MAX - reserved for activity tracing aware ETW sessions
+ // perEventSourceSessionId-1 represents the bit in the reserved field (bits 44..47) in
+ // Keywords that identifies the session
+ // perEventSourceSessionId = SessionMask.MAX+1 - reserved for legacy ETW sessions; these are
+ // discriminated by etwSessionId
+ // * etwSessionId specifies a machine-wide ETW session ID; this allows correlation of
+ // activity tracing across different providers (which might have different sessionIds
+ // for the same ETW session)
+ // * enable, level, matchAnyKeywords are used to set a default for all events for the
+ // eventSource. In particular, if 'enabled' is false, 'level' and
+ // 'matchAnyKeywords' are not used.
+ // * OnEventCommand is invoked, which may cause calls to
+ // code:EventSource.EnableEventForDispatcher which may cause changes in the filtering
+ // depending on the logic in that routine.
+ // * else (command != Update)
+ // * Simply call OnEventCommand. The expectation is that filtering is NOT changed.
+ // * The 'enabled' 'level', matchAnyKeyword' arguments are ignored (must be true, 0, 0).
+ //
+ // dispatcher == null has special meaning. It is the 'ETW' dispatcher.
+ internal void SendCommand(EventListener listener, int perEventSourceSessionId, int etwSessionId,
+ EventCommand command, bool enable,
+ EventLevel level, EventKeywords matchAnyKeyword,
+ IDictionary<string, string> commandArguments)
+ {
+ var commandArgs = new EventCommandEventArgs(command, commandArguments, this, listener, perEventSourceSessionId, etwSessionId, enable, level, matchAnyKeyword);
+ lock (EventListener.EventListenersLock)
+ {
+ if (m_completelyInited)
+ {
+ // After the first command arrive after construction, we are ready to get rid of the deferred commands
+ this.m_deferredCommands = null;
+ // We are fully initialized, do the command
+ DoCommand(commandArgs);
+ }
+ else
+ {
+ // We can't do the command, simply remember it and we do it when we are fully constructed.
+ commandArgs.nextCommand = m_deferredCommands;
+ m_deferredCommands = commandArgs;
+ }
+ }
+ }
+
+ /// <summary>
+ /// We want the eventSource to be fully initialized when we do commands because that way we can send
+ /// error messages and other logging directly to the event stream. Unfortunately we can get callbacks
+ /// when we are not fully initialized. In that case we store them in 'commandArgs' and do them later.
+ /// This helper actually does all actual command logic.
+ /// </summary>
+ internal void DoCommand(EventCommandEventArgs commandArgs)
+ {
+ // PRECONDITION: We should be holding the EventListener.EventListenersLock
+ // We defer commands until we are completely inited. This allows error messages to be sent.
+ Debug.Assert(m_completelyInited);
+
+#if FEATURE_MANAGED_ETW
+ if (m_provider == null) // If we failed to construct
+ return;
+#endif // FEATURE_MANAGED_ETW
+
+ m_outOfBandMessageCount = 0;
+ bool shouldReport = (commandArgs.perEventSourceSessionId > 0) && (commandArgs.perEventSourceSessionId <= SessionMask.MAX);
+ try
+ {
+ EnsureDescriptorsInitialized();
+ Debug.Assert(m_eventData != null);
+
+ // Find the per-EventSource dispatcher corresponding to registered dispatcher
+ commandArgs.dispatcher = GetDispatcher(commandArgs.listener);
+ if (commandArgs.dispatcher == null && commandArgs.listener != null) // dispatcher == null means ETW dispatcher
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_ListenerNotFound"));
+ }
+
+ if (commandArgs.Arguments == null)
+ commandArgs.Arguments = new Dictionary<string, string>();
+
+ if (commandArgs.Command == EventCommand.Update)
+ {
+ // Set it up using the 'standard' filtering bitfields (use the "global" enable, not session specific one)
+ for (int i = 0; i < m_eventData.Length; i++)
+ EnableEventForDispatcher(commandArgs.dispatcher, i, IsEnabledByDefault(i, commandArgs.enable, commandArgs.level, commandArgs.matchAnyKeyword));
+
+ if (commandArgs.enable)
+ {
+ if (!m_eventSourceEnabled)
+ {
+ // EventSource turned on for the first time, simply copy the bits.
+ m_level = commandArgs.level;
+ m_matchAnyKeyword = commandArgs.matchAnyKeyword;
+ }
+ else
+ {
+ // Already enabled, make it the most verbose of the existing and new filter
+ if (commandArgs.level > m_level)
+ m_level = commandArgs.level;
+ if (commandArgs.matchAnyKeyword == 0)
+ m_matchAnyKeyword = 0;
+ else if (m_matchAnyKeyword != 0)
+ m_matchAnyKeyword = unchecked(m_matchAnyKeyword | commandArgs.matchAnyKeyword);
+ }
+ }
+
+ // interpret perEventSourceSessionId's sign, and adjust perEventSourceSessionId to
+ // represent 0-based positive values
+ bool bSessionEnable = (commandArgs.perEventSourceSessionId >= 0);
+ if (commandArgs.perEventSourceSessionId == 0 && commandArgs.enable == false)
+ bSessionEnable = false;
+
+ if (commandArgs.listener == null)
+ {
+ if (!bSessionEnable)
+ commandArgs.perEventSourceSessionId = -commandArgs.perEventSourceSessionId;
+ // for "global" enable/disable (passed in with listener == null and
+ // perEventSourceSessionId == 0) perEventSourceSessionId becomes -1
+ --commandArgs.perEventSourceSessionId;
+ }
+
+ commandArgs.Command = bSessionEnable ? EventCommand.Enable : EventCommand.Disable;
+
+ // perEventSourceSessionId = -1 when ETW sent a notification, but the set of active sessions
+ // hasn't changed.
+ // sesisonId = SessionMask.MAX when one of the legacy ETW sessions changed
+ // 0 <= perEventSourceSessionId < SessionMask.MAX for activity-tracing aware sessions
+ Debug.Assert(commandArgs.perEventSourceSessionId >= -1 && commandArgs.perEventSourceSessionId <= SessionMask.MAX);
+
+ // Send the manifest if we are enabling an ETW session
+ if (bSessionEnable && commandArgs.dispatcher == null)
+ {
+ // eventSourceDispatcher == null means this is the ETW manifest
+
+ // Note that we unconditionally send the manifest whenever we are enabled, even if
+ // we were already enabled. This is because there may be multiple sessions active
+ // and we can't know that all the sessions have seen the manifest.
+ if (!SelfDescribingEvents)
+ SendManifest(m_rawManifest);
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ if (bSessionEnable && commandArgs.perEventSourceSessionId != -1)
+ {
+ bool participateInSampling = false;
+ string activityFilters;
+ int sessionIdBit;
+
+ ParseCommandArgs(commandArgs.Arguments, out participateInSampling,
+ out activityFilters, out sessionIdBit);
+
+ if (commandArgs.listener == null && commandArgs.Arguments.Count > 0 && commandArgs.perEventSourceSessionId != sessionIdBit)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_SessionIdError",
+ commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD,
+ sessionIdBit + SessionMask.SHIFT_SESSION_TO_KEYWORD));
+ }
+
+ if (commandArgs.listener == null)
+ {
+ UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, true, activityFilters, participateInSampling);
+ }
+ else
+ {
+ ActivityFilter.UpdateFilter(ref commandArgs.listener.m_activityFilter, this, 0, activityFilters);
+ commandArgs.dispatcher.m_activityFilteringEnabled = participateInSampling;
+ }
+ }
+ else if (!bSessionEnable && commandArgs.listener == null)
+ {
+ // if we disable an ETW session, indicate that in a synthesized command argument
+ if (commandArgs.perEventSourceSessionId >= 0 && commandArgs.perEventSourceSessionId < SessionMask.MAX)
+ {
+ commandArgs.Arguments["EtwSessionKeyword"] = (commandArgs.perEventSourceSessionId + SessionMask.SHIFT_SESSION_TO_KEYWORD).ToString(CultureInfo.InvariantCulture);
+ }
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // Turn on the enable bit before making the OnEventCommand callback This allows you to do useful
+ // things like log messages, or test if keywords are enabled in the callback.
+ if (commandArgs.enable)
+ {
+ Debug.Assert(m_eventData != null);
+ m_eventSourceEnabled = true;
+ }
+
+ this.OnEventCommand(commandArgs);
+ var eventCommandCallback = this.m_eventCommandExecuted;
+ if (eventCommandCallback != null)
+ eventCommandCallback(this, commandArgs);
+
+#if FEATURE_ACTIVITYSAMPLING
+ if (commandArgs.listener == null && !bSessionEnable && commandArgs.perEventSourceSessionId != -1)
+ {
+ // if we disable an ETW session, complete disabling it
+ UpdateEtwSession(commandArgs.perEventSourceSessionId, commandArgs.etwSessionId, false, null, false);
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ if (!commandArgs.enable)
+ {
+ // If we are disabling, maybe we can turn on 'quick checks' to filter
+ // quickly. These are all just optimizations (since later checks will still filter)
+
+#if FEATURE_ACTIVITYSAMPLING
+ // Turn off (and forget) any information about Activity Tracing.
+ if (commandArgs.listener == null)
+ {
+ // reset all filtering information for activity-tracing-aware sessions
+ for (int i = 0; i < SessionMask.MAX; ++i)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[i];
+ if (etwSession != null)
+ ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
+ }
+ m_activityFilteringForETWEnabled = new SessionMask(0);
+ m_curLiveSessions = new SessionMask(0);
+ // reset activity-tracing-aware sessions
+ if (m_etwSessionIdMap != null)
+ for (int i = 0; i < SessionMask.MAX; ++i)
+ m_etwSessionIdMap[i] = null;
+ // reset legacy sessions
+ if (m_legacySessions != null)
+ m_legacySessions.Clear();
+ }
+ else
+ {
+ ActivityFilter.DisableFilter(ref commandArgs.listener.m_activityFilter, this);
+ commandArgs.dispatcher.m_activityFilteringEnabled = false;
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // There is a good chance EnabledForAnyListener are not as accurate as
+ // they could be, go ahead and get a better estimate.
+ for (int i = 0; i < m_eventData.Length; i++)
+ {
+ bool isEnabledForAnyListener = false;
+ for (EventDispatcher dispatcher = m_Dispatchers; dispatcher != null; dispatcher = dispatcher.m_Next)
+ {
+ if (dispatcher.m_EventEnabled[i])
+ {
+ isEnabledForAnyListener = true;
+ break;
+ }
+ }
+ m_eventData[i].EnabledForAnyListener = isEnabledForAnyListener;
+ }
+
+ // If no events are enabled, disable the global enabled bit.
+ if (!AnyEventEnabled())
+ {
+ m_level = 0;
+ m_matchAnyKeyword = 0;
+ m_eventSourceEnabled = false;
+ }
+ }
+#if FEATURE_ACTIVITYSAMPLING
+ UpdateKwdTriggers(commandArgs.enable);
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+ else
+ {
+ if (commandArgs.Command == EventCommand.SendManifest)
+ {
+ // TODO: should we generate the manifest here if we hadn't already?
+ if (m_rawManifest != null)
+ SendManifest(m_rawManifest);
+ }
+
+ // These are not used for non-update commands and thus should always be 'default' values
+ // Debug.Assert(enable == true);
+ // Debug.Assert(level == EventLevel.LogAlways);
+ // Debug.Assert(matchAnyKeyword == EventKeywords.None);
+
+ this.OnEventCommand(commandArgs);
+ var eventCommandCallback = m_eventCommandExecuted;
+ if (eventCommandCallback != null)
+ eventCommandCallback(this, commandArgs);
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ if (m_completelyInited && (commandArgs.listener != null || shouldReport))
+ {
+ SessionMask m = SessionMask.FromId(commandArgs.perEventSourceSessionId);
+ ReportActivitySamplingInfo(commandArgs.listener, m);
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+ }
+ catch (Exception e)
+ {
+ // When the ETW session is created after the EventSource has registered with the ETW system
+ // we can send any error messages here.
+ ReportOutOfBandMessage("ERROR: Exception in Command Processing for EventSource " + Name + ": " + e.Message, true);
+ // We never throw when doing a command.
+ }
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+
+ internal void UpdateEtwSession(
+ int sessionIdBit,
+ int etwSessionId,
+ bool bEnable,
+ string activityFilters,
+ bool participateInSampling)
+ {
+ if (sessionIdBit < SessionMask.MAX)
+ {
+ // activity-tracing-aware etw session
+ if (bEnable)
+ {
+ var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
+ ActivityFilter.UpdateFilter(ref etwSession.m_activityFilter, this, sessionIdBit, activityFilters);
+ m_etwSessionIdMap[sessionIdBit] = etwSession;
+ m_activityFilteringForETWEnabled[sessionIdBit] = participateInSampling;
+ }
+ else
+ {
+ var etwSession = EtwSession.GetEtwSession(etwSessionId);
+ m_etwSessionIdMap[sessionIdBit] = null;
+ m_activityFilteringForETWEnabled[sessionIdBit] = false;
+ if (etwSession != null)
+ {
+ ActivityFilter.DisableFilter(ref etwSession.m_activityFilter, this);
+ // the ETW session is going away; remove it from the global list
+ EtwSession.RemoveEtwSession(etwSession);
+ }
+ }
+ m_curLiveSessions[sessionIdBit] = bEnable;
+ }
+ else
+ {
+ // legacy etw session
+ if (bEnable)
+ {
+ if (m_legacySessions == null)
+ m_legacySessions = new List<EtwSession>(8);
+ var etwSession = EtwSession.GetEtwSession(etwSessionId, true);
+ if (!m_legacySessions.Contains(etwSession))
+ m_legacySessions.Add(etwSession);
+ }
+ else
+ {
+ var etwSession = EtwSession.GetEtwSession(etwSessionId);
+ if (etwSession != null)
+ {
+ if (m_legacySessions != null)
+ m_legacySessions.Remove(etwSession);
+ // the ETW session is going away; remove it from the global list
+ EtwSession.RemoveEtwSession(etwSession);
+ }
+ }
+ }
+ }
+
+ internal static bool ParseCommandArgs(
+ IDictionary<string, string> commandArguments,
+ out bool participateInSampling,
+ out string activityFilters,
+ out int sessionIdBit)
+ {
+ bool res = true;
+ participateInSampling = false;
+ string activityFilterString;
+ if (commandArguments.TryGetValue("ActivitySamplingStartEvent", out activityFilters))
+ {
+ // if a start event is specified default the event source to participate in sampling
+ participateInSampling = true;
+ }
+
+ if (commandArguments.TryGetValue("ActivitySampling", out activityFilterString))
+ {
+ if (string.Compare(activityFilterString, "false", StringComparison.OrdinalIgnoreCase) == 0 ||
+ activityFilterString == "0")
+ participateInSampling = false;
+ else
+ participateInSampling = true;
+ }
+
+ string sSessionKwd;
+ int sessionKwd = -1;
+ if (!commandArguments.TryGetValue("EtwSessionKeyword", out sSessionKwd) ||
+ !int.TryParse(sSessionKwd, out sessionKwd) ||
+ sessionKwd < SessionMask.SHIFT_SESSION_TO_KEYWORD ||
+ sessionKwd >= SessionMask.SHIFT_SESSION_TO_KEYWORD + SessionMask.MAX)
+ {
+ sessionIdBit = -1;
+ res = false;
+ }
+ else
+ {
+ sessionIdBit = sessionKwd - SessionMask.SHIFT_SESSION_TO_KEYWORD;
+ }
+ return res;
+ }
+
+ internal void UpdateKwdTriggers(bool enable)
+ {
+ if (enable)
+ {
+ // recompute m_keywordTriggers
+ ulong gKeywords = unchecked((ulong)m_matchAnyKeyword);
+ if (gKeywords == 0)
+ gKeywords = 0xFFFFffffFFFFffff;
+
+ m_keywordTriggers = 0;
+ for (int sessId = 0; sessId < SessionMask.MAX; ++sessId)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[sessId];
+ if (etwSession == null)
+ continue;
+
+ ActivityFilter activityFilter = etwSession.m_activityFilter;
+ ActivityFilter.UpdateKwdTriggers(activityFilter, m_guid, this, unchecked((EventKeywords)gKeywords));
+ }
+ }
+ else
+ {
+ m_keywordTriggers = 0;
+ }
+ }
+
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ /// <summary>
+ /// If 'value is 'true' then set the eventSource so that 'dispatcher' will receive event with the eventId
+ /// of 'eventId. If value is 'false' disable the event for that dispatcher. If 'eventId' is out of
+ /// range return false, otherwise true.
+ /// </summary>
+ internal bool EnableEventForDispatcher(EventDispatcher dispatcher, int eventId, bool value)
+ {
+ if (dispatcher == null)
+ {
+ if (eventId >= m_eventData.Length)
+ return false;
+#if FEATURE_MANAGED_ETW
+ if (m_provider != null)
+ m_eventData[eventId].EnabledForETW = value;
+#endif
+ }
+ else
+ {
+ if (eventId >= dispatcher.m_EventEnabled.Length)
+ return false;
+ dispatcher.m_EventEnabled[eventId] = value;
+ if (value)
+ m_eventData[eventId].EnabledForAnyListener = true;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Returns true if any event at all is on.
+ /// </summary>
+ private bool AnyEventEnabled()
+ {
+ for (int i = 0; i < m_eventData.Length; i++)
+ if (m_eventData[i].EnabledForETW || m_eventData[i].EnabledForAnyListener)
+ return true;
+ return false;
+ }
+
+ private bool IsDisposed
+ {
+ get { return m_eventSourceDisposed; }
+ }
+
+ private void EnsureDescriptorsInitialized()
+ {
+#if !ES_BUILD_STANDALONE
+ Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+ if (m_eventData == null)
+ {
+ Guid eventSourceGuid = Guid.Empty;
+ string eventSourceName = null;
+ EventMetadata[] eventData = null;
+ byte[] manifest = null;
+
+ // Try the GetMetadata provided by the ILTransform in ProjectN. The default sets all to null, and in that case we fall back
+ // to the reflection approach.
+ GetMetadata(out eventSourceGuid, out eventSourceName, out eventData, out manifest);
+
+ if (eventSourceGuid.Equals(Guid.Empty) || eventSourceName == null || eventData == null || manifest == null)
+ {
+ // GetMetadata failed, so we have to set it via reflection.
+ Debug.Assert(m_rawManifest == null);
+ m_rawManifest = CreateManifestAndDescriptors(this.GetType(), Name, this);
+ Debug.Assert(m_eventData != null);
+
+ }
+ else
+ {
+ // GetMetadata worked, so set the fields as appropriate.
+ m_name = eventSourceName;
+ m_guid = eventSourceGuid;
+ m_eventData = eventData;
+ m_rawManifest = manifest;
+ }
+ // TODO Enforce singleton pattern
+ foreach (WeakReference eventSourceRef in EventListener.s_EventSources)
+ {
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource != null && eventSource.Guid == m_guid && !eventSource.IsDisposed)
+ {
+ if (eventSource != this)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_EventSourceGuidInUse", m_guid));
+ }
+ }
+ }
+
+ // Make certain all dispatchers also have their arrays initialized
+ EventDispatcher dispatcher = m_Dispatchers;
+ while (dispatcher != null)
+ {
+ if (dispatcher.m_EventEnabled == null)
+ dispatcher.m_EventEnabled = new bool[m_eventData.Length];
+ dispatcher = dispatcher.m_Next;
+ }
+ }
+ if (s_currentPid == 0)
+ {
+#if ES_BUILD_STANDALONE && !ES_BUILD_PCL && !CORECLR
+ // for non-BCL EventSource we must assert SecurityPermission
+ new SecurityPermission(PermissionState.Unrestricted).Assert();
+#endif
+ s_currentPid = Win32Native.GetCurrentProcessId();
+ }
+ }
+
+ // Send out the ETW manifest XML out to ETW
+ // Today, we only send the manifest to ETW, custom listeners don't get it.
+ private unsafe bool SendManifest(byte[] rawManifest)
+ {
+ bool success = true;
+
+ if (rawManifest == null)
+ return false;
+
+ Debug.Assert(!SelfDescribingEvents);
+
+#if FEATURE_MANAGED_ETW
+ fixed (byte* dataPtr = rawManifest)
+ {
+ // we don't want the manifest to show up in the event log channels so we specify as keywords
+ // everything but the first 8 bits (reserved for the 8 channels)
+ var manifestDescr = new EventDescriptor(0xFFFE, 1, 0, 0, 0xFE, 0xFFFE, 0x00ffFFFFffffFFFF);
+ ManifestEnvelope envelope = new ManifestEnvelope();
+
+ envelope.Format = ManifestEnvelope.ManifestFormats.SimpleXmlFormat;
+ envelope.MajorVersion = 1;
+ envelope.MinorVersion = 0;
+ envelope.Magic = 0x5B; // An unusual number that can be checked for consistency.
+ int dataLeft = rawManifest.Length;
+ envelope.ChunkNumber = 0;
+
+ EventProvider.EventData* dataDescrs = stackalloc EventProvider.EventData[2];
+
+ dataDescrs[0].Ptr = (ulong)&envelope;
+ dataDescrs[0].Size = (uint)sizeof(ManifestEnvelope);
+ dataDescrs[0].Reserved = 0;
+
+ dataDescrs[1].Ptr = (ulong)dataPtr;
+ dataDescrs[1].Reserved = 0;
+
+ int chunkSize = ManifestEnvelope.MaxChunkSize;
+ TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE:
+ envelope.TotalChunks = (ushort)((dataLeft + (chunkSize - 1)) / chunkSize);
+ while (dataLeft > 0)
+ {
+ dataDescrs[1].Size = (uint)Math.Min(dataLeft, chunkSize);
+ if (m_provider != null)
+ {
+ if (!m_provider.WriteEvent(ref manifestDescr, null, null, 2, (IntPtr)dataDescrs))
+ {
+ // Turns out that if users set the BufferSize to something less than 64K then WriteEvent
+ // can fail. If we get this failure on the first chunk try again with something smaller
+ // The smallest BufferSize is 1K so if we get to 256 (to account for envelope overhead), we can give up making it smaller.
+ if (EventProvider.GetLastWriteEventError() == EventProvider.WriteEventErrorCode.EventTooBig)
+ {
+ if (envelope.ChunkNumber == 0 && chunkSize > 256)
+ {
+ chunkSize = chunkSize / 2;
+ goto TRY_AGAIN_WITH_SMALLER_CHUNK_SIZE;
+ }
+ }
+ success = false;
+ if (ThrowOnEventWriteErrors)
+ ThrowEventSourceException("SendManifest");
+ break;
+ }
+ }
+ dataLeft -= chunkSize;
+ dataDescrs[1].Ptr += (uint)chunkSize;
+ envelope.ChunkNumber++;
+
+ // For large manifests we want to not overflow any receiver's buffer. Most manifests will fit within
+ // 5 chunks, so only the largest manifests will hit the pause.
+ if ((envelope.ChunkNumber % 5) == 0)
+ {
+ RuntimeThread.Sleep(15);
+ }
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ return success;
+ }
+
+#if (ES_BUILD_PCL)
+ internal static Attribute GetCustomAttributeHelper(Type type, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
+ {
+ return GetCustomAttributeHelper(type.GetTypeInfo(), attributeType, flags);
+ }
+#endif
+
+ // Helper to deal with the fact that the type we are reflecting over might be loaded in the ReflectionOnly context.
+ // When that is the case, we have the build the custom assemblies on a member by hand.
+ internal static Attribute GetCustomAttributeHelper(MemberInfo member, Type attributeType, EventManifestOptions flags = EventManifestOptions.None)
+ {
+ if (!member.Module.Assembly.ReflectionOnly() && (flags & EventManifestOptions.AllowEventSourceOverride) == 0)
+ {
+ // Let the runtime to the work for us, since we can execute code in this context.
+ Attribute firstAttribute = null;
+ foreach (var attribute in member.GetCustomAttributes(attributeType, false))
+ {
+ firstAttribute = (Attribute)attribute;
+ break;
+ }
+ return firstAttribute;
+ }
+
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+ // In the reflection only context, we have to do things by hand.
+ string fullTypeNameToFind = attributeType.FullName;
+
+#if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
+ fullTypeNameToFind = fullTypeNameToFind.Replace("System.Diagnostics.Eventing", "System.Diagnostics.Tracing");
+#endif
+
+ foreach (CustomAttributeData data in CustomAttributeData.GetCustomAttributes(member))
+ {
+ if (AttributeTypeNamesMatch(attributeType, data.Constructor.ReflectedType))
+ {
+ Attribute attr = null;
+
+ Debug.Assert(data.ConstructorArguments.Count <= 1);
+
+ if (data.ConstructorArguments.Count == 1)
+ {
+ attr = (Attribute)Activator.CreateInstance(attributeType, new object[] { data.ConstructorArguments[0].Value });
+ }
+ else if (data.ConstructorArguments.Count == 0)
+ {
+ attr = (Attribute)Activator.CreateInstance(attributeType);
+ }
+
+ if (attr != null)
+ {
+ Type t = attr.GetType();
+
+ foreach (CustomAttributeNamedArgument namedArgument in data.NamedArguments)
+ {
+ PropertyInfo p = t.GetProperty(namedArgument.MemberInfo.Name, BindingFlags.Public | BindingFlags.Instance);
+ object value = namedArgument.TypedValue.Value;
+
+ if (p.PropertyType.IsEnum)
+ {
+ value = Enum.Parse(p.PropertyType, value.ToString());
+ }
+
+ p.SetValue(attr, value, null);
+ }
+
+ return attr;
+ }
+ }
+ }
+
+ return null;
+#else // ES_BUILD_PCL && ES_BUILD_PN
+ // Don't use nameof here because the resource doesn't exist on some platforms, which results in a compilation error.
+ throw new ArgumentException(Resources.GetResourceString("EventSource", "EventSource_PCLPlatformNotSupportedReflection"));
+#endif
+ }
+
+ /// <summary>
+ /// Evaluates if two related "EventSource"-domain types should be considered the same
+ /// </summary>
+ /// <param name="attributeType">The attribute type in the load context - it's associated with the running
+ /// EventSource type. This type may be different fromt he base type of the user-defined EventSource.</param>
+ /// <param name="reflectedAttributeType">The attribute type in the reflection context - it's associated with
+ /// the user-defined EventSource, and is in the same assembly as the eventSourceType passed to
+ /// </param>
+ /// <returns>True - if the types should be considered equivalent, False - otherwise</returns>
+ private static bool AttributeTypeNamesMatch(Type attributeType, Type reflectedAttributeType)
+ {
+ return
+ // are these the same type?
+ attributeType == reflectedAttributeType ||
+ // are the full typenames equal?
+ string.Equals(attributeType.FullName, reflectedAttributeType.FullName, StringComparison.Ordinal) ||
+ // are the typenames equal and the namespaces under "Diagnostics.Tracing" (typically
+ // either Microsoft.Diagnostics.Tracing or System.Diagnostics.Tracing)?
+ string.Equals(attributeType.Name, reflectedAttributeType.Name, StringComparison.Ordinal) &&
+ attributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal) &&
+ (reflectedAttributeType.Namespace.EndsWith("Diagnostics.Tracing", StringComparison.Ordinal)
+#if EVENT_SOURCE_LEGACY_NAMESPACE_SUPPORT
+ || reflectedAttributeType.Namespace.EndsWith("Diagnostics.Eventing", StringComparison.Ordinal)
+#endif
+);
+ }
+
+ private static Type GetEventSourceBaseType(Type eventSourceType, bool allowEventSourceOverride, bool reflectionOnly)
+ {
+ // return false for "object" and interfaces
+ if (eventSourceType.BaseType() == null)
+ return null;
+
+ // now go up the inheritance chain until hitting a concrete type ("object" at worse)
+ do
+ {
+ eventSourceType = eventSourceType.BaseType();
+ }
+ while (eventSourceType != null && eventSourceType.IsAbstract());
+
+ if (eventSourceType != null)
+ {
+ if (!allowEventSourceOverride)
+ {
+ if (reflectionOnly && eventSourceType.FullName != typeof(EventSource).FullName ||
+ !reflectionOnly && eventSourceType != typeof(EventSource))
+ return null;
+ }
+ else
+ {
+ if (eventSourceType.Name != "EventSource")
+ return null;
+ }
+ }
+ return eventSourceType;
+ }
+
+ // Use reflection to look at the attributes of a class, and generate a manifest for it (as UTF8) and
+ // return the UTF8 bytes. It also sets up the code:EventData structures needed to dispatch events
+ // at run time. 'source' is the event source to place the descriptors. If it is null,
+ // then the descriptors are not creaed, and just the manifest is generated.
+ private static byte[] CreateManifestAndDescriptors(Type eventSourceType, string eventSourceDllName, EventSource source,
+ EventManifestOptions flags = EventManifestOptions.None)
+ {
+ ManifestBuilder manifest = null;
+ bool bNeedsManifest = source != null ? !source.SelfDescribingEvents : true;
+ Exception exception = null; // exception that might get raised during validation b/c we couldn't/didn't recover from a previous error
+ byte[] res = null;
+
+ if (eventSourceType.IsAbstract() && (flags & EventManifestOptions.Strict) == 0)
+ return null;
+
+#if DEBUG && ES_BUILD_STANDALONE
+ TestSupport.TestHooks.MaybeThrow(eventSourceType,
+ TestSupport.Category.ManifestError,
+ "EventSource_CreateManifestAndDescriptors",
+ new ArgumentException("EventSource_CreateManifestAndDescriptors"));
+#endif
+
+ try
+ {
+ MethodInfo[] methods = eventSourceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
+ EventAttribute defaultEventAttribute;
+ int eventId = 1; // The number given to an event that does not have a explicitly given ID.
+ EventMetadata[] eventData = null;
+ Dictionary<string, string> eventsByName = null;
+ if (source != null || (flags & EventManifestOptions.Strict) != 0)
+ {
+ eventData = new EventMetadata[methods.Length + 1];
+ eventData[0].Name = ""; // Event 0 is the 'write messages string' event, and has an empty name.
+ }
+
+ // See if we have localization information.
+ ResourceManager resources = null;
+ EventSourceAttribute eventSourceAttrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags);
+ if (eventSourceAttrib != null && eventSourceAttrib.LocalizationResources != null)
+ resources = new ResourceManager(eventSourceAttrib.LocalizationResources, eventSourceType.Assembly());
+
+ manifest = new ManifestBuilder(GetName(eventSourceType, flags), GetGuid(eventSourceType), eventSourceDllName,
+ resources, flags);
+
+ // Add an entry unconditionally for event ID 0 which will be for a string message.
+ manifest.StartEvent("EventSourceMessage", new EventAttribute(0) { Level = EventLevel.LogAlways, Task = (EventTask)0xFFFE });
+ manifest.AddEventParameter(typeof(string), "message");
+ manifest.EndEvent();
+
+ // eventSourceType must be sealed and must derive from this EventSource
+ if ((flags & EventManifestOptions.Strict) != 0)
+ {
+ bool typeMatch = GetEventSourceBaseType(eventSourceType, (flags & EventManifestOptions.AllowEventSourceOverride) != 0, eventSourceType.Assembly().ReflectionOnly()) != null;
+
+ if (!typeMatch)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_TypeMustDeriveFromEventSource"));
+ }
+ if (!eventSourceType.IsAbstract() && !eventSourceType.IsSealed())
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_TypeMustBeSealedOrAbstract"));
+ }
+ }
+
+ // Collect task, opcode, keyword and channel information
+#if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes", "Channels" })
+#else
+ foreach (var providerEnumKind in new string[] { "Keywords", "Tasks", "Opcodes" })
+#endif
+ {
+ Type nestedType = eventSourceType.GetNestedType(providerEnumKind);
+ if (nestedType != null)
+ {
+ if (eventSourceType.IsAbstract())
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_AbstractMustNotDeclareKTOC", nestedType.Name));
+ }
+ else
+ {
+ foreach (FieldInfo staticField in nestedType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
+ {
+ AddProviderEnumKind(manifest, staticField, providerEnumKind);
+ }
+ }
+ }
+ }
+ // ensure we have keywords for the session-filtering reserved bits
+ {
+ manifest.AddKeyword("Session3", (long)0x1000 << 32);
+ manifest.AddKeyword("Session2", (long)0x2000 << 32);
+ manifest.AddKeyword("Session1", (long)0x4000 << 32);
+ manifest.AddKeyword("Session0", (long)0x8000 << 32);
+ }
+
+ if (eventSourceType != typeof(EventSource))
+ {
+ for (int i = 0; i < methods.Length; i++)
+ {
+ MethodInfo method = methods[i];
+ ParameterInfo[] args = method.GetParameters();
+
+ // Get the EventDescriptor (from the Custom attributes)
+ EventAttribute eventAttribute = (EventAttribute)GetCustomAttributeHelper(method, typeof(EventAttribute), flags);
+
+ // Compat: until v4.5.1 we ignored any non-void returning methods as well as virtual methods for
+ // the only reason of limiting the number of methods considered to be events. This broke a common
+ // design of having event sources implement specific interfaces. To fix this in a compatible way
+ // we will now allow both non-void returning and virtual methods to be Event methods, as long
+ // as they are marked with the [Event] attribute
+ if (/* method.IsVirtual || */ method.IsStatic)
+ {
+ continue;
+ }
+
+ if (eventSourceType.IsAbstract())
+ {
+ if (eventAttribute != null)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_AbstractMustNotDeclareEventMethods", method.Name, eventAttribute.EventId));
+ }
+ continue;
+ }
+ else if (eventAttribute == null)
+ {
+ // Methods that don't return void can't be events, if they're NOT marked with [Event].
+ // (see Compat comment above)
+ if (method.ReturnType != typeof(void))
+ {
+ continue;
+ }
+
+ // Continue to ignore virtual methods if they do NOT have the [Event] attribute
+ // (see Compat comment above)
+ if (method.IsVirtual)
+ {
+ continue;
+ }
+
+ // If we explicitly mark the method as not being an event, then honor that.
+ if (GetCustomAttributeHelper(method, typeof(NonEventAttribute), flags) != null)
+ continue;
+
+ defaultEventAttribute = new EventAttribute(eventId);
+ eventAttribute = defaultEventAttribute;
+ }
+ else if (eventAttribute.EventId <= 0)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_NeedPositiveId", method.Name), true);
+ continue; // don't validate anything else for this event
+ }
+ if (method.Name.LastIndexOf('.') >= 0)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EventMustNotBeExplicitImplementation", method.Name, eventAttribute.EventId));
+ }
+
+ eventId++;
+ string eventName = method.Name;
+
+ if (eventAttribute.Opcode == EventOpcode.Info) // We are still using the default opcode.
+ {
+ // By default pick a task ID derived from the EventID, starting with the highest task number and working back
+ bool noTask = (eventAttribute.Task == EventTask.None);
+ if (noTask)
+ eventAttribute.Task = (EventTask)(0xFFFE - eventAttribute.EventId);
+
+ // Unless we explicitly set the opcode to Info (to override the auto-generate of Start or Stop opcodes,
+ // pick a default opcode based on the event name (either Info or start or stop if the name ends with that suffix).
+ if (!eventAttribute.IsOpcodeSet)
+ eventAttribute.Opcode = GetOpcodeWithDefault(EventOpcode.Info, eventName);
+
+ // Make the stop opcode have the same task as the start opcode.
+ if (noTask)
+ {
+ if (eventAttribute.Opcode == EventOpcode.Start)
+ {
+ string taskName = eventName.Substring(0, eventName.Length - s_ActivityStartSuffix.Length); // Remove the Stop suffix to get the task name
+ if (string.Compare(eventName, 0, taskName, 0, taskName.Length) == 0 &&
+ string.Compare(eventName, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(eventName.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
+ {
+ // Add a task that is just the task name for the start event. This suppress the auto-task generation
+ // That would otherwise happen (and create 'TaskName'Start as task name rather than just 'TaskName'
+ manifest.AddTask(taskName, (int)eventAttribute.Task);
+ }
+ }
+ else if (eventAttribute.Opcode == EventOpcode.Stop)
+ {
+ // Find the start associated with this stop event. We require start to be immediately before the stop
+ int startEventId = eventAttribute.EventId - 1;
+ if (eventData != null && startEventId < eventData.Length)
+ {
+ Debug.Assert(0 <= startEventId); // Since we reserve id 0, we know that id-1 is <= 0
+ EventMetadata startEventMetadata = eventData[startEventId];
+
+ // If you remove the Stop and add a Start does that name match the Start Event's Name?
+ // Ideally we would throw an error
+ string taskName = eventName.Substring(0, eventName.Length - s_ActivityStopSuffix.Length); // Remove the Stop suffix to get the task name
+ if (startEventMetadata.Descriptor.Opcode == (byte)EventOpcode.Start &&
+ string.Compare(startEventMetadata.Name, 0, taskName, 0, taskName.Length) == 0 &&
+ string.Compare(startEventMetadata.Name, taskName.Length, s_ActivityStartSuffix, 0, Math.Max(startEventMetadata.Name.Length - taskName.Length, s_ActivityStartSuffix.Length)) == 0)
+ {
+
+ // Make the stop event match the start event
+ eventAttribute.Task = (EventTask)startEventMetadata.Descriptor.Task;
+ noTask = false;
+ }
+ }
+ if (noTask && (flags & EventManifestOptions.Strict) != 0) // Throw an error if we can compatibly.
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_StopsFollowStarts"));
+ }
+ }
+ }
+ }
+
+ bool hasRelatedActivityID = RemoveFirstArgIfRelatedActivityId(ref args);
+ if (!(source != null && source.SelfDescribingEvents))
+ {
+ manifest.StartEvent(eventName, eventAttribute);
+ for (int fieldIdx = 0; fieldIdx < args.Length; fieldIdx++)
+ {
+ manifest.AddEventParameter(args[fieldIdx].ParameterType, args[fieldIdx].Name);
+ }
+ manifest.EndEvent();
+ }
+
+ if (source != null || (flags & EventManifestOptions.Strict) != 0)
+ {
+ // Do checking for user errors (optional, but not a big deal so we do it).
+ DebugCheckEvent(ref eventsByName, eventData, method, eventAttribute, manifest, flags);
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // add the channel keyword for Event Viewer channel based filters. This is added for creating the EventDescriptors only
+ // and is not required for the manifest
+ if (eventAttribute.Channel != EventChannel.None)
+ {
+ unchecked
+ {
+ eventAttribute.Keywords |= (EventKeywords)manifest.GetChannelKeyword(eventAttribute.Channel, (ulong)eventAttribute.Keywords);
+ }
+ }
+#endif
+ string eventKey = "event_" + eventName;
+ string msg = manifest.GetLocalizedMessage(eventKey, CultureInfo.CurrentUICulture, etwFormat: false);
+ // overwrite inline message with the localized message
+ if (msg != null) eventAttribute.Message = msg;
+
+ AddEventDescriptor(ref eventData, eventName, eventAttribute, args, hasRelatedActivityID);
+ }
+ }
+ }
+
+ // Tell the TraceLogging stuff where to start allocating its own IDs.
+ NameInfo.ReserveEventIDsBelow(eventId);
+
+ if (source != null)
+ {
+ TrimEventDescriptors(ref eventData);
+ source.m_eventData = eventData; // officially initialize it. We do this at most once (it is racy otherwise).
+#if FEATURE_MANAGED_ETW_CHANNELS
+ source.m_channelData = manifest.GetChannelData();
+#endif
+ }
+
+ // if this is an abstract event source we've already performed all the validation we can
+ if (!eventSourceType.IsAbstract() && (source == null || !source.SelfDescribingEvents))
+ {
+ bNeedsManifest = (flags & EventManifestOptions.OnlyIfNeededForRegistration) == 0
+#if FEATURE_MANAGED_ETW_CHANNELS
+ || manifest.GetChannelData().Length > 0
+#endif
+;
+
+ // if the manifest is not needed and we're not requested to validate the event source return early
+ if (!bNeedsManifest && (flags & EventManifestOptions.Strict) == 0)
+ return null;
+
+ res = manifest.CreateManifest();
+ }
+ }
+ catch (Exception e)
+ {
+ // if this is a runtime manifest generation let the exception propagate
+ if ((flags & EventManifestOptions.Strict) == 0)
+ throw;
+ // else store it to include it in the Argument exception we raise below
+ exception = e;
+ }
+
+ if ((flags & EventManifestOptions.Strict) != 0 && (manifest.Errors.Count > 0 || exception != null))
+ {
+ string msg = String.Empty;
+ if (manifest.Errors.Count > 0)
+ {
+ bool firstError = true;
+ foreach (string error in manifest.Errors)
+ {
+ if (!firstError)
+ msg += Environment.NewLine;
+ firstError = false;
+ msg += error;
+ }
+ }
+ else
+ msg = "Unexpected error: " + exception.Message;
+
+ throw new ArgumentException(msg, exception);
+ }
+
+ return bNeedsManifest ? res : null;
+ }
+
+ private static bool RemoveFirstArgIfRelatedActivityId(ref ParameterInfo[] args)
+ {
+ // If the first parameter is (case insensitive) 'relatedActivityId' then skip it.
+ if (args.Length > 0 && args[0].ParameterType == typeof(Guid) &&
+ string.Compare(args[0].Name, "relatedActivityId", StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ var newargs = new ParameterInfo[args.Length - 1];
+ Array.Copy(args, 1, newargs, 0, args.Length - 1);
+ args = newargs;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // adds a enumeration (keyword, opcode, task or channel) represented by 'staticField'
+ // to the manifest.
+ private static void AddProviderEnumKind(ManifestBuilder manifest, FieldInfo staticField, string providerEnumKind)
+ {
+ bool reflectionOnly = staticField.Module.Assembly.ReflectionOnly();
+ Type staticFieldType = staticField.FieldType;
+ if (!reflectionOnly && (staticFieldType == typeof(EventOpcode)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventOpcode)))
+ {
+ if (providerEnumKind != "Opcodes") goto Error;
+ int value = (int)staticField.GetRawConstantValue();
+ manifest.AddOpcode(staticField.Name, value);
+ }
+ else if (!reflectionOnly && (staticFieldType == typeof(EventTask)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventTask)))
+ {
+ if (providerEnumKind != "Tasks") goto Error;
+ int value = (int)staticField.GetRawConstantValue();
+ manifest.AddTask(staticField.Name, value);
+ }
+ else if (!reflectionOnly && (staticFieldType == typeof(EventKeywords)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventKeywords)))
+ {
+ if (providerEnumKind != "Keywords") goto Error;
+ ulong value = unchecked((ulong)(long)staticField.GetRawConstantValue());
+ manifest.AddKeyword(staticField.Name, value);
+ }
+#if FEATURE_MANAGED_ETW_CHANNELS && FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ else if (!reflectionOnly && (staticFieldType == typeof(EventChannel)) || AttributeTypeNamesMatch(staticFieldType, typeof(EventChannel)))
+ {
+ if (providerEnumKind != "Channels") goto Error;
+ var channelAttribute = (EventChannelAttribute)GetCustomAttributeHelper(staticField, typeof(EventChannelAttribute));
+ manifest.AddChannel(staticField.Name, (byte)staticField.GetRawConstantValue(), channelAttribute);
+ }
+#endif
+ return;
+ Error:
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EnumKindMismatch", staticField.Name, staticField.FieldType.Name, providerEnumKind));
+ }
+
+ // Helper used by code:CreateManifestAndDescriptors to add a code:EventData descriptor for a method
+ // with the code:EventAttribute 'eventAttribute'. resourceManger may be null in which case we populate it
+ // it is populated if we need to look up message resources
+ private static void AddEventDescriptor(ref EventMetadata[] eventData, string eventName,
+ EventAttribute eventAttribute, ParameterInfo[] eventParameters,
+ bool hasRelatedActivityID)
+ {
+ if (eventData == null || eventData.Length <= eventAttribute.EventId)
+ {
+ EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)];
+ Array.Copy(eventData, 0, newValues, 0, eventData.Length);
+ eventData = newValues;
+ }
+
+ eventData[eventAttribute.EventId].Descriptor = new EventDescriptor(
+ eventAttribute.EventId,
+ eventAttribute.Version,
+#if FEATURE_MANAGED_ETW_CHANNELS
+ (byte)eventAttribute.Channel,
+#else
+ (byte)0,
+#endif
+ (byte)eventAttribute.Level,
+ (byte)eventAttribute.Opcode,
+ (int)eventAttribute.Task,
+ unchecked((long)((ulong)eventAttribute.Keywords | SessionMask.All.ToEventKeywords())));
+
+ eventData[eventAttribute.EventId].Tags = eventAttribute.Tags;
+ eventData[eventAttribute.EventId].Name = eventName;
+ eventData[eventAttribute.EventId].Parameters = eventParameters;
+ eventData[eventAttribute.EventId].Message = eventAttribute.Message;
+ eventData[eventAttribute.EventId].ActivityOptions = eventAttribute.ActivityOptions;
+ eventData[eventAttribute.EventId].HasRelatedActivityID = hasRelatedActivityID;
+ }
+
+ // Helper used by code:CreateManifestAndDescriptors that trims the m_eventData array to the correct
+ // size after all event descriptors have been added.
+ private static void TrimEventDescriptors(ref EventMetadata[] eventData)
+ {
+ int idx = eventData.Length;
+ while (0 < idx)
+ {
+ --idx;
+ if (eventData[idx].Descriptor.EventId != 0)
+ break;
+ }
+ if (eventData.Length - idx > 2) // allow one wasted slot.
+ {
+ EventMetadata[] newValues = new EventMetadata[idx + 1];
+ Array.Copy(eventData, 0, newValues, 0, newValues.Length);
+ eventData = newValues;
+ }
+ }
+
+ // Helper used by code:EventListener.AddEventSource and code:EventListener.EventListener
+ // when a listener gets attached to a eventSource
+ internal void AddListener(EventListener listener)
+ {
+ lock (EventListener.EventListenersLock)
+ {
+ bool[] enabledArray = null;
+ if (m_eventData != null)
+ enabledArray = new bool[m_eventData.Length];
+ m_Dispatchers = new EventDispatcher(m_Dispatchers, enabledArray, listener);
+ listener.OnEventSourceCreated(this);
+ }
+ }
+
+ // Helper used by code:CreateManifestAndDescriptors to find user mistakes like reusing an event
+ // index for two distinct events etc. Throws exceptions when it finds something wrong.
+ private static void DebugCheckEvent(ref Dictionary<string, string> eventsByName,
+ EventMetadata[] eventData, MethodInfo method, EventAttribute eventAttribute,
+ ManifestBuilder manifest, EventManifestOptions options)
+ {
+ int evtId = eventAttribute.EventId;
+ string evtName = method.Name;
+ int eventArg = GetHelperCallFirstArg(method);
+ if (eventArg >= 0 && evtId != eventArg)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_MismatchIdToWriteEvent", evtName, evtId, eventArg), true);
+ }
+
+ if (evtId < eventData.Length && eventData[evtId].Descriptor.EventId != 0)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EventIdReused", evtName, evtId, eventData[evtId].Name), true);
+ }
+
+ // We give a task to things if they don't have one.
+ // TODO this is moderately expensive (N*N). We probably should not even bother....
+ Debug.Assert(eventAttribute.Task != EventTask.None || eventAttribute.Opcode != EventOpcode.Info);
+ for (int idx = 0; idx < eventData.Length; ++idx)
+ {
+ // skip unused Event IDs.
+ if (eventData[idx].Name == null)
+ continue;
+
+ if (eventData[idx].Descriptor.Task == (int)eventAttribute.Task && eventData[idx].Descriptor.Opcode == (int)eventAttribute.Opcode)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_TaskOpcodePairReused",
+ evtName, evtId, eventData[idx].Name, idx));
+ // If we are not strict stop on first error. We have had problems with really large providers taking forever. because of many errors.
+ if ((options & EventManifestOptions.Strict) == 0)
+ break;
+ }
+ }
+
+ // for non-default event opcodes the user must define a task!
+ if (eventAttribute.Opcode != EventOpcode.Info)
+ {
+ bool failure = false;
+ if (eventAttribute.Task == EventTask.None)
+ failure = true;
+ else
+ {
+ // If you have the auto-assigned Task, then you did not explicitly set one.
+ // This is OK for Start events because we have special logic to assign the task to a prefix derived from the event name
+ // But all other cases we want to catch the omission.
+ var autoAssignedTask = (EventTask)(0xFFFE - evtId);
+ if ((eventAttribute.Opcode != EventOpcode.Start && eventAttribute.Opcode != EventOpcode.Stop) && eventAttribute.Task == autoAssignedTask)
+ failure = true;
+ }
+ if (failure)
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EventMustHaveTaskIfNonDefaultOpcode", evtName, evtId));
+ }
+ }
+
+ // If we ever want to enforce the rule: MethodName = TaskName + OpcodeName here's how:
+ // (the reason we don't is backwards compat and the need for handling this as a non-fatal error
+ // by eventRegister.exe)
+ // taskName & opcodeName could be passed in by the caller which has opTab & taskTab handy
+ // if (!(((int)eventAttribute.Opcode == 0 && evtName == taskName) || (evtName == taskName+opcodeName)))
+ // {
+ // throw new WarningException(Resources.GetResourceString("EventSource_EventNameDoesNotEqualTaskPlusOpcode"));
+ // }
+
+ if (eventsByName == null)
+ eventsByName = new Dictionary<string, string>();
+
+ if (eventsByName.ContainsKey(evtName))
+ {
+ manifest.ManifestError(Resources.GetResourceString("EventSource_EventNameReused", evtName), true);
+ }
+
+ eventsByName[evtName] = evtName;
+ }
+
+ /// <summary>
+ /// This method looks at the IL and tries to pattern match against the standard
+ /// 'boilerplate' event body
+ /// <code>
+ /// { if (Enabled()) WriteEvent(#, ...) }
+ /// </code>
+ /// If the pattern matches, it returns the literal number passed as the first parameter to
+ /// the WriteEvent. This is used to find common user errors (mismatching this
+ /// number with the EventAttribute ID). It is only used for validation.
+ /// </summary>
+ /// <param name="method">The method to probe.</param>
+ /// <returns>The literal value or -1 if the value could not be determined. </returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Switch statement is clearer than alternatives")]
+ static private int GetHelperCallFirstArg(MethodInfo method)
+ {
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+ // Currently searches for the following pattern
+ //
+ // ... // CAN ONLY BE THE INSTRUCTIONS BELOW
+ // LDARG0
+ // LDC.I4 XXX
+ // ... // CAN ONLY BE THE INSTRUCTIONS BELOW CAN'T BE A BRANCH OR A CALL
+ // CALL
+ // NOP // 0 or more times
+ // RET
+ //
+ // If we find this pattern we return the XXX. Otherwise we return -1.
+#if !CORECLR
+ (new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)).Assert();
+#endif
+ byte[] instrs = method.GetMethodBody().GetILAsByteArray();
+ int retVal = -1;
+ for (int idx = 0; idx < instrs.Length;)
+ {
+ switch (instrs[idx])
+ {
+ case 0: // NOP
+ case 1: // BREAK
+ case 2: // LDARG_0
+ case 3: // LDARG_1
+ case 4: // LDARG_2
+ case 5: // LDARG_3
+ case 6: // LDLOC_0
+ case 7: // LDLOC_1
+ case 8: // LDLOC_2
+ case 9: // LDLOC_3
+ case 10: // STLOC_0
+ case 11: // STLOC_1
+ case 12: // STLOC_2
+ case 13: // STLOC_3
+ break;
+ case 14: // LDARG_S
+ case 16: // STARG_S
+ idx++;
+ break;
+ case 20: // LDNULL
+ break;
+ case 21: // LDC_I4_M1
+ case 22: // LDC_I4_0
+ case 23: // LDC_I4_1
+ case 24: // LDC_I4_2
+ case 25: // LDC_I4_3
+ case 26: // LDC_I4_4
+ case 27: // LDC_I4_5
+ case 28: // LDC_I4_6
+ case 29: // LDC_I4_7
+ case 30: // LDC_I4_8
+ if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
+ retVal = instrs[idx] - 22;
+ break;
+ case 31: // LDC_I4_S
+ if (idx > 0 && instrs[idx - 1] == 2) // preceeded by LDARG0
+ retVal = instrs[idx + 1];
+ idx++;
+ break;
+ case 32: // LDC_I4
+ idx += 4;
+ break;
+ case 37: // DUP
+ break;
+ case 40: // CALL
+ idx += 4;
+
+ if (retVal >= 0)
+ {
+ // Is this call just before return?
+ for (int search = idx + 1; search < instrs.Length; search++)
+ {
+ if (instrs[search] == 42) // RET
+ return retVal;
+ if (instrs[search] != 0) // NOP
+ break;
+ }
+ }
+ retVal = -1;
+ break;
+ case 44: // BRFALSE_S
+ case 45: // BRTRUE_S
+ retVal = -1;
+ idx++;
+ break;
+ case 57: // BRFALSE
+ case 58: // BRTRUE
+ retVal = -1;
+ idx += 4;
+ break;
+ case 103: // CONV_I1
+ case 104: // CONV_I2
+ case 105: // CONV_I4
+ case 106: // CONV_I8
+ case 109: // CONV_U4
+ case 110: // CONV_U8
+ break;
+ case 140: // BOX
+ case 141: // NEWARR
+ idx += 4;
+ break;
+ case 162: // STELEM_REF
+ break;
+ case 254: // PREFIX
+ idx++;
+ // Covers the CEQ instructions used in debug code for some reason.
+ if (idx >= instrs.Length || instrs[idx] >= 6)
+ goto default;
+ break;
+ default:
+ /* Debug.Assert(false, "Warning: User validation code sub-optimial: Unsuported opcode " + instrs[idx] +
+ " at " + idx + " in method " + method.Name); */
+ return -1;
+ }
+ idx++;
+ }
+#endif
+ return -1;
+ }
+
+#if false // This routine is not needed at all, it was used for unit test debugging.
+ [Conditional("DEBUG")]
+ private static void OutputDebugString(string msg)
+ {
+#if !ES_BUILD_PCL
+ msg = msg.TrimEnd('\r', '\n') +
+ string.Format(CultureInfo.InvariantCulture, ", Thrd({0})" + Environment.NewLine, Thread.CurrentThread.ManagedThreadId);
+ System.Diagnostics.Debugger.Log(0, null, msg);
+#endif
+ }
+#endif
+
+ /// <summary>
+ /// Sends an error message to the debugger (outputDebugString), as well as the EventListeners
+ /// It will do this even if the EventSource is not enabled.
+ /// TODO remove flush parameter it is not used.
+ /// </summary>
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "This does not need to be correct when racing with other threads")]
+ internal void ReportOutOfBandMessage(string msg, bool flush)
+ {
+ try
+ {
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+ // send message to debugger without delay
+ System.Diagnostics.Debugger.Log(0, null, String.Format("EventSource Error: {0}{1}", msg, Environment.NewLine));
+#endif
+
+ // Send it to all listeners.
+ if (m_outOfBandMessageCount < 16 - 1) // Note this is only if size byte
+ m_outOfBandMessageCount++;
+ else
+ {
+ if (m_outOfBandMessageCount == 16)
+ return;
+ m_outOfBandMessageCount = 16; // Mark that we hit the limit. Notify them that this is the case.
+ msg = "Reached message limit. End of EventSource error messages.";
+ }
+
+ WriteEventString(EventLevel.LogAlways, -1, msg);
+ WriteStringToAllListeners("EventSourceMessage", msg);
+ }
+ catch (Exception) { } // If we fail during last chance logging, well, we have to give up....
+ }
+
+ private EventSourceSettings ValidateSettings(EventSourceSettings settings)
+ {
+ var evtFormatMask = EventSourceSettings.EtwManifestEventFormat |
+ EventSourceSettings.EtwSelfDescribingEventFormat;
+ if ((settings & evtFormatMask) == evtFormatMask)
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidEventFormat"), nameof(settings));
+ }
+
+ // If you did not explicitly ask for manifest, you get self-describing.
+ if ((settings & evtFormatMask) == 0)
+ settings |= EventSourceSettings.EtwSelfDescribingEventFormat;
+ return settings;
+ }
+
+ private bool ThrowOnEventWriteErrors
+ {
+ get { return (m_config & EventSourceSettings.ThrowOnEventWriteErrors) != 0; }
+ set
+ {
+ if (value) m_config |= EventSourceSettings.ThrowOnEventWriteErrors;
+ else m_config &= ~EventSourceSettings.ThrowOnEventWriteErrors;
+ }
+ }
+
+ private bool SelfDescribingEvents
+ {
+ get
+ {
+ Debug.Assert(((m_config & EventSourceSettings.EtwManifestEventFormat) != 0) !=
+ ((m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0));
+ return (m_config & EventSourceSettings.EtwSelfDescribingEventFormat) != 0;
+ }
+ set
+ {
+ if (!value)
+ {
+ m_config |= EventSourceSettings.EtwManifestEventFormat;
+ m_config &= ~EventSourceSettings.EtwSelfDescribingEventFormat;
+ }
+ else
+ {
+ m_config |= EventSourceSettings.EtwSelfDescribingEventFormat;
+ m_config &= ~EventSourceSettings.EtwManifestEventFormat;
+ }
+ }
+ }
+
+#if FEATURE_ACTIVITYSAMPLING
+ private void ReportActivitySamplingInfo(EventListener listener, SessionMask sessions)
+ {
+ Debug.Assert(listener == null || (uint)sessions == (uint)SessionMask.FromId(0));
+
+ for (int perEventSourceSessionId = 0; perEventSourceSessionId < SessionMask.MAX; ++perEventSourceSessionId)
+ {
+ if (!sessions[perEventSourceSessionId])
+ continue;
+
+ ActivityFilter af;
+ if (listener == null)
+ {
+ EtwSession etwSession = m_etwSessionIdMap[perEventSourceSessionId];
+ Debug.Assert(etwSession != null);
+ af = etwSession.m_activityFilter;
+ }
+ else
+ {
+ af = listener.m_activityFilter;
+ }
+
+ if (af == null)
+ continue;
+
+ SessionMask m = new SessionMask();
+ m[perEventSourceSessionId] = true;
+
+ foreach (var t in af.GetFilterAsTuple(m_guid))
+ {
+ WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: {1} = {2}", perEventSourceSessionId, t.Item1, t.Item2), m);
+ }
+
+ bool participateInSampling = (listener == null) ?
+ m_activityFilteringForETWEnabled[perEventSourceSessionId] :
+ GetDispatcher(listener).m_activityFilteringEnabled;
+ WriteStringToListener(listener, string.Format(CultureInfo.InvariantCulture, "Session {0}: Activity Sampling support: {1}",
+ perEventSourceSessionId, participateInSampling ? "enabled" : "disabled"), m);
+ }
+ }
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // private instance state
+ private string m_name; // My friendly name (privided in ctor)
+ internal int m_id; // A small integer that is unique to this instance.
+ private Guid m_guid; // GUID representing the ETW eventSource to the OS.
+ internal volatile EventMetadata[] m_eventData; // None per-event data
+ private volatile byte[] m_rawManifest; // Bytes to send out representing the event schema
+
+ private EventHandler<EventCommandEventArgs> m_eventCommandExecuted;
+
+ private EventSourceSettings m_config; // configuration information
+
+ private bool m_eventSourceDisposed; // has Dispose been called.
+
+ // Enabling bits
+ private bool m_eventSourceEnabled; // am I enabled (any of my events are enabled for any dispatcher)
+ internal EventLevel m_level; // highest level enabled by any output dispatcher
+ internal EventKeywords m_matchAnyKeyword; // the logical OR of all levels enabled by any output dispatcher (zero is a special case) meaning 'all keywords'
+
+ // Dispatching state
+ internal volatile EventDispatcher m_Dispatchers; // Linked list of code:EventDispatchers we write the data to (we also do ETW specially)
+#if FEATURE_MANAGED_ETW
+ private volatile OverideEventProvider m_provider; // This hooks up ETW commands to our 'OnEventCommand' callback
+#endif
+ private bool m_completelyInited; // The EventSource constructor has returned without exception.
+ private Exception m_constructionException; // If there was an exception construction, this is it
+ private byte m_outOfBandMessageCount; // The number of out of band messages sent (we throttle them
+ private EventCommandEventArgs m_deferredCommands;// If we get commands before we are fully we store them here and run the when we are fully inited.
+
+ private string[] m_traits; // Used to implement GetTraits
+
+ internal static uint s_currentPid; // current process id, used in synthesizing quasi-GUIDs
+ [ThreadStatic]
+ private static byte m_EventSourceExceptionRecurenceCount = 0; // current recursion count inside ThrowEventSourceException
+
+ [ThreadStatic]
+ private static bool m_EventSourceInDecodeObject = false;
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ internal volatile ulong[] m_channelData;
+#endif
+
+#if FEATURE_ACTIVITYSAMPLING
+ private SessionMask m_curLiveSessions; // the activity-tracing aware sessions' bits
+ private EtwSession[] m_etwSessionIdMap; // the activity-tracing aware sessions
+ private List<EtwSession> m_legacySessions; // the legacy ETW sessions listening to this source
+ internal long m_keywordTriggers; // a bit is set if it corresponds to a keyword that's part of an enabled triggering event
+ internal SessionMask m_activityFilteringForETWEnabled; // does THIS EventSource have activity filtering turned on for each ETW session
+ static internal Action<Guid> s_activityDying; // Fires when something calls SetCurrentThreadToActivity()
+ // Also used to mark that activity tracing is on for some case
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // We use a single instance of ActivityTracker for all EventSources instances to allow correlation between multiple event providers.
+ // We have m_activityTracker field simply because instance field is more efficient than static field fetch.
+ ActivityTracker m_activityTracker;
+ internal const string s_ActivityStartSuffix = "Start";
+ internal const string s_ActivityStopSuffix = "Stop";
+
+ // used for generating GUID from eventsource name
+ private static readonly byte[] namespaceBytes = new byte[] {
+ 0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
+ 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
+ };
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Enables specifying event source configuration options to be used in the EventSource constructor.
+ /// </summary>
+ [Flags]
+ public enum EventSourceSettings
+ {
+ /// <summary>
+ /// This specifies none of the special configuration options should be enabled.
+ /// </summary>
+ Default = 0,
+ /// <summary>
+ /// Normally an EventSource NEVER throws; setting this option will tell it to throw when it encounters errors.
+ /// </summary>
+ ThrowOnEventWriteErrors = 1,
+ /// <summary>
+ /// Setting this option is a directive to the ETW listener should use manifest-based format when
+ /// firing events. This is the default option when defining a type derived from EventSource
+ /// (using the protected EventSource constructors).
+ /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
+ /// </summary>
+ EtwManifestEventFormat = 4,
+ /// <summary>
+ /// Setting this option is a directive to the ETW listener should use self-describing event format
+ /// when firing events. This is the default option when creating a new instance of the EventSource
+ /// type (using the public EventSource constructors).
+ /// Only one of EtwManifestEventFormat or EtwSelfDescribingEventFormat should be specified
+ /// </summary>
+ EtwSelfDescribingEventFormat = 8,
+ }
+
+ /// <summary>
+ /// An EventListener represents a target for the events generated by EventSources (that is subclasses
+ /// of <see cref="EventSource"/>), in the current appdomain. When a new EventListener is created
+ /// it is logically attached to all eventSources in that appdomain. When the EventListener is Disposed, then
+ /// it is disconnected from the event eventSources. Note that there is a internal list of STRONG references
+ /// to EventListeners, which means that relying on the lack of references to EventListeners to clean up
+ /// EventListeners will NOT work. You must call EventListener.Dispose explicitly when a dispatcher is no
+ /// longer needed.
+ /// <para>
+ /// Once created, EventListeners can enable or disable on a per-eventSource basis using verbosity levels
+ /// (<see cref="EventLevel"/>) and bitfields (<see cref="EventKeywords"/>) to further restrict the set of
+ /// events to be sent to the dispatcher. The dispatcher can also send arbitrary commands to a particular
+ /// eventSource using the 'SendCommand' method. The meaning of the commands are eventSource specific.
+ /// </para><para>
+ /// The Null Guid (that is (new Guid()) has special meaning as a wildcard for 'all current eventSources in
+ /// the appdomain'. Thus it is relatively easy to turn on all events in the appdomain if desired.
+ /// </para><para>
+ /// It is possible for there to be many EventListener's defined in a single appdomain. Each dispatcher is
+ /// logically independent of the other listeners. Thus when one dispatcher enables or disables events, it
+ /// affects only that dispatcher (other listeners get the events they asked for). It is possible that
+ /// commands sent with 'SendCommand' would do a semantic operation that would affect the other listeners
+ /// (like doing a GC, or flushing data ...), but this is the exception rather than the rule.
+ /// </para><para>
+ /// Thus the model is that each EventSource keeps a list of EventListeners that it is sending events
+ /// to. Associated with each EventSource-dispatcher pair is a set of filtering criteria that determine for
+ /// that eventSource what events that dispatcher will receive.
+ /// </para><para>
+ /// Listeners receive the events on their 'OnEventWritten' method. Thus subclasses of EventListener must
+ /// override this method to do something useful with the data.
+ /// </para><para>
+ /// In addition, when new eventSources are created, the 'OnEventSourceCreate' method is called. The
+ /// invariant associated with this callback is that every eventSource gets exactly one
+ /// 'OnEventSourceCreate' call for ever eventSource that can potentially send it log messages. In
+ /// particular when a EventListener is created, typically a series of OnEventSourceCreate' calls are
+ /// made to notify the new dispatcher of all the eventSources that existed before the EventListener was
+ /// created.
+ /// </para>
+ /// </summary>
+ public class EventListener : IDisposable
+ {
+ private event EventHandler<EventSourceCreatedEventArgs> _EventSourceCreated;
+
+ /// <summary>
+ /// This event is raised whenever a new eventSource is 'attached' to the dispatcher.
+ /// This can happen for all existing EventSources when the EventListener is created
+ /// as well as for any EventSources that come into existence after the EventListener
+ /// has been created.
+ ///
+ /// These 'catch up' events are called during the construction of the EventListener.
+ /// Subclasses need to be prepared for that.
+ ///
+ /// In a multi-threaded environment, it is possible that 'EventSourceEventWrittenCallback'
+ /// events for a particular eventSource to occur BEFORE the EventSourceCreatedCallback is issued.
+ /// </summary>
+ public event EventHandler<EventSourceCreatedEventArgs> EventSourceCreated
+ {
+ add
+ {
+ CallBackForExistingEventSources(false, value);
+
+ this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Combine(_EventSourceCreated, value);
+ }
+ remove
+ {
+ this._EventSourceCreated = (EventHandler<EventSourceCreatedEventArgs>)Delegate.Remove(_EventSourceCreated, value);
+ }
+ }
+
+ /// <summary>
+ /// This event is raised whenever an event has been written by a EventSource for which
+ /// the EventListener has enabled events.
+ /// </summary>
+ public event EventHandler<EventWrittenEventArgs> EventWritten;
+
+ /// <summary>
+ /// Create a new EventListener in which all events start off turned off (use EnableEvents to turn
+ /// them on).
+ /// </summary>
+ public EventListener()
+ {
+ // This will cause the OnEventSourceCreated callback to fire.
+ CallBackForExistingEventSources(true, (obj, args) => args.EventSource.AddListener((EventListener)obj));
+ }
+
+ /// <summary>
+ /// Dispose should be called when the EventListener no longer desires 'OnEvent*' callbacks. Because
+ /// there is an internal list of strong references to all EventListeners, calling 'Dispose' directly
+ /// is the only way to actually make the listen die. Thus it is important that users of EventListener
+ /// call Dispose when they are done with their logging.
+ /// </summary>
+#if ES_BUILD_STANDALONE
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
+#endif
+ public virtual void Dispose()
+ {
+ lock (EventListenersLock)
+ {
+ if (s_Listeners != null)
+ {
+ if (this == s_Listeners)
+ {
+ EventListener cur = s_Listeners;
+ s_Listeners = this.m_Next;
+ RemoveReferencesToListenerInEventSources(cur);
+ }
+ else
+ {
+ // Find 'this' from the s_Listeners linked list.
+ EventListener prev = s_Listeners;
+ for (;;)
+ {
+ EventListener cur = prev.m_Next;
+ if (cur == null)
+ break;
+ if (cur == this)
+ {
+ // Found our Listener, remove references to to it in the eventSources
+ prev.m_Next = cur.m_Next; // Remove entry.
+ RemoveReferencesToListenerInEventSources(cur);
+ break;
+ }
+ prev = cur;
+ }
+ }
+ }
+ Validate();
+ }
+ }
+ // We don't expose a Dispose(bool), because the contract is that you don't have any non-syncronous
+ // 'cleanup' associated with this object
+
+ /// <summary>
+ /// Enable all events from the eventSource identified by 'eventSource' to the current
+ /// dispatcher that have a verbosity level of 'level' or lower.
+ ///
+ /// This call can have the effect of REDUCING the number of events sent to the
+ /// dispatcher if 'level' indicates a less verbose level than was previously enabled.
+ ///
+ /// This call never has an effect on other EventListeners.
+ ///
+ /// </summary>
+ public void EnableEvents(EventSource eventSource, EventLevel level)
+ {
+ EnableEvents(eventSource, level, EventKeywords.None);
+ }
+ /// <summary>
+ /// Enable all events from the eventSource identified by 'eventSource' to the current
+ /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
+ /// matching any of the bits in 'matchAnyKeyword'.
+ ///
+ /// This call can have the effect of REDUCING the number of events sent to the
+ /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
+ /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
+ ///
+ /// This call never has an effect on other EventListeners.
+ /// </summary>
+ public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword)
+ {
+ EnableEvents(eventSource, level, matchAnyKeyword, null);
+ }
+ /// <summary>
+ /// Enable all events from the eventSource identified by 'eventSource' to the current
+ /// dispatcher that have a verbosity level of 'level' or lower and have a event keyword
+ /// matching any of the bits in 'matchAnyKeyword' as well as any (eventSource specific)
+ /// effect passing additional 'key-value' arguments 'arguments' might have.
+ ///
+ /// This call can have the effect of REDUCING the number of events sent to the
+ /// dispatcher if 'level' indicates a less verbose level than was previously enabled or
+ /// if 'matchAnyKeyword' has fewer keywords set than where previously set.
+ ///
+ /// This call never has an effect on other EventListeners.
+ /// </summary>
+ public void EnableEvents(EventSource eventSource, EventLevel level, EventKeywords matchAnyKeyword, IDictionary<string, string> arguments)
+ {
+ if (eventSource == null)
+ {
+ throw new ArgumentNullException(nameof(eventSource));
+ }
+ Contract.EndContractBlock();
+
+ eventSource.SendCommand(this, 0, 0, EventCommand.Update, true, level, matchAnyKeyword, arguments);
+ }
+ /// <summary>
+ /// Disables all events coming from eventSource identified by 'eventSource'.
+ ///
+ /// This call never has an effect on other EventListeners.
+ /// </summary>
+ public void DisableEvents(EventSource eventSource)
+ {
+ if (eventSource == null)
+ {
+ throw new ArgumentNullException(nameof(eventSource));
+ }
+ Contract.EndContractBlock();
+
+ eventSource.SendCommand(this, 0, 0, EventCommand.Update, false, EventLevel.LogAlways, EventKeywords.None, null);
+ }
+
+ /// <summary>
+ /// EventSourceIndex is small non-negative integer (suitable for indexing in an array)
+ /// identifying EventSource. It is unique per-appdomain. Some EventListeners might find
+ /// it useful to store additional information about each eventSource connected to it,
+ /// and EventSourceIndex allows this extra information to be efficiently stored in a
+ /// (growable) array (eg List(T)).
+ /// </summary>
+ public static int EventSourceIndex(EventSource eventSource) { return eventSource.m_id; }
+
+ /// <summary>
+ /// This method is called whenever a new eventSource is 'attached' to the dispatcher.
+ /// This can happen for all existing EventSources when the EventListener is created
+ /// as well as for any EventSources that come into existence after the EventListener
+ /// has been created.
+ ///
+ /// These 'catch up' events are called during the construction of the EventListener.
+ /// Subclasses need to be prepared for that.
+ ///
+ /// In a multi-threaded environment, it is possible that 'OnEventWritten' callbacks
+ /// for a particular eventSource to occur BEFORE the OnEventSourceCreated is issued.
+ /// </summary>
+ /// <param name="eventSource"></param>
+ internal protected virtual void OnEventSourceCreated(EventSource eventSource)
+ {
+ EventHandler<EventSourceCreatedEventArgs> callBack = this._EventSourceCreated;
+ if (callBack != null)
+ {
+ EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
+ args.EventSource = eventSource;
+ callBack(this, args);
+ }
+ }
+
+ /// <summary>
+ /// This method is called whenever an event has been written by a EventSource for which
+ /// the EventListener has enabled events.
+ /// </summary>
+ /// <param name="eventData"></param>
+ internal protected virtual void OnEventWritten(EventWrittenEventArgs eventData)
+ {
+ EventHandler<EventWrittenEventArgs> callBack = this.EventWritten;
+ if (callBack != null)
+ {
+ callBack(this, eventData);
+ }
+ }
+
+
+ #region private
+ /// <summary>
+ /// This routine adds newEventSource to the global list of eventSources, it also assigns the
+ /// ID to the eventSource (which is simply the ordinal in the global list).
+ ///
+ /// EventSources currently do not pro-actively remove themselves from this list. Instead
+ /// when eventSources's are GCed, the weak handle in this list naturally gets nulled, and
+ /// we will reuse the slot. Today this list never shrinks (but we do reuse entries
+ /// that are in the list). This seems OK since the expectation is that EventSources
+ /// tend to live for the lifetime of the appdomain anyway (they tend to be used in
+ /// global variables).
+ /// </summary>
+ /// <param name="newEventSource"></param>
+ internal static void AddEventSource(EventSource newEventSource)
+ {
+ lock (EventListenersLock)
+ {
+ if (s_EventSources == null)
+ s_EventSources = new List<WeakReference>(2);
+
+ if (!s_EventSourceShutdownRegistered)
+ {
+ s_EventSourceShutdownRegistered = true;
+ }
+
+
+ // Periodically search the list for existing entries to reuse, this avoids
+ // unbounded memory use if we keep recycling eventSources (an unlikely thing).
+ int newIndex = -1;
+ if (s_EventSources.Count % 64 == 63) // on every block of 64, fill up the block before continuing
+ {
+ int i = s_EventSources.Count; // Work from the top down.
+ while (0 < i)
+ {
+ --i;
+ WeakReference weakRef = s_EventSources[i];
+ if (!weakRef.IsAlive)
+ {
+ newIndex = i;
+ weakRef.Target = newEventSource;
+ break;
+ }
+ }
+ }
+ if (newIndex < 0)
+ {
+ newIndex = s_EventSources.Count;
+ s_EventSources.Add(new WeakReference(newEventSource));
+ }
+ newEventSource.m_id = newIndex;
+
+ // Add every existing dispatcher to the new EventSource
+ for (EventListener listener = s_Listeners; listener != null; listener = listener.m_Next)
+ newEventSource.AddListener(listener);
+
+ Validate();
+ }
+ }
+
+ // Whenver we have async callbacks from native code, there is an ugly issue where
+ // during .NET shutdown native code could be calling the callback, but the CLR
+ // has already prohibited callbacks to managed code in the appdomain, causing the CLR
+ // to throw a COMPLUS_BOOT_EXCEPTION. The guideline we give is that you must unregister
+ // such callbacks on process shutdown or appdomain so that unmanaged code will never
+ // do this. This is what this callback is for.
+ // See bug 724140 for more
+ private static void DisposeOnShutdown(object sender, EventArgs e)
+ {
+ lock (EventListenersLock)
+ {
+ foreach (var esRef in s_EventSources)
+ {
+ EventSource es = esRef.Target as EventSource;
+ if (es != null)
+ es.Dispose();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Helper used in code:Dispose that removes any references to 'listenerToRemove' in any of the
+ /// eventSources in the appdomain.
+ ///
+ /// The EventListenersLock must be held before calling this routine.
+ /// </summary>
+ private static void RemoveReferencesToListenerInEventSources(EventListener listenerToRemove)
+ {
+#if !ES_BUILD_STANDALONE
+ Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+ // Foreach existing EventSource in the appdomain
+ foreach (WeakReference eventSourceRef in s_EventSources)
+ {
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource != null)
+ {
+ // Is the first output dispatcher the dispatcher we are removing?
+ if (eventSource.m_Dispatchers.m_Listener == listenerToRemove)
+ eventSource.m_Dispatchers = eventSource.m_Dispatchers.m_Next;
+ else
+ {
+ // Remove 'listenerToRemove' from the eventSource.m_Dispatchers linked list.
+ EventDispatcher prev = eventSource.m_Dispatchers;
+ for (;;)
+ {
+ EventDispatcher cur = prev.m_Next;
+ if (cur == null)
+ {
+ Debug.Assert(false, "EventSource did not have a registered EventListener!");
+ break;
+ }
+ if (cur.m_Listener == listenerToRemove)
+ {
+ prev.m_Next = cur.m_Next; // Remove entry.
+ break;
+ }
+ prev = cur;
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Checks internal consistency of EventSources/Listeners.
+ /// </summary>
+ [Conditional("DEBUG")]
+ internal static void Validate()
+ {
+ lock (EventListenersLock)
+ {
+ // Get all listeners
+ Dictionary<EventListener, bool> allListeners = new Dictionary<EventListener, bool>();
+ EventListener cur = s_Listeners;
+ while (cur != null)
+ {
+ allListeners.Add(cur, true);
+ cur = cur.m_Next;
+ }
+
+ // For all eventSources
+ int id = -1;
+ foreach (WeakReference eventSourceRef in s_EventSources)
+ {
+ id++;
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource == null)
+ continue;
+ Debug.Assert(eventSource.m_id == id, "Unexpected event source ID.");
+
+ // None listeners on eventSources exist in the dispatcher list.
+ EventDispatcher dispatcher = eventSource.m_Dispatchers;
+ while (dispatcher != null)
+ {
+ Debug.Assert(allListeners.ContainsKey(dispatcher.m_Listener), "EventSource has a listener not on the global list.");
+ dispatcher = dispatcher.m_Next;
+ }
+
+ // Every dispatcher is on Dispatcher List of every eventSource.
+ foreach (EventListener listener in allListeners.Keys)
+ {
+ dispatcher = eventSource.m_Dispatchers;
+ for (;;)
+ {
+ Debug.Assert(dispatcher != null, "Listener is not on all eventSources.");
+ if (dispatcher.m_Listener == listener)
+ break;
+ dispatcher = dispatcher.m_Next;
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a global lock that is intended to protect the code:s_Listeners linked list and the
+ /// code:s_EventSources WeakReference list. (We happen to use the s_EventSources list as
+ /// the lock object)
+ /// </summary>
+ internal static object EventListenersLock
+ {
+ get
+ {
+ if (s_EventSources == null)
+ Interlocked.CompareExchange(ref s_EventSources, new List<WeakReference>(2), null);
+ return s_EventSources;
+ }
+ }
+
+ private void CallBackForExistingEventSources(bool addToListenersList, EventHandler<EventSourceCreatedEventArgs> callback)
+ {
+ lock (EventListenersLock)
+ {
+ // Disallow creating EventListener reentrancy.
+ if (s_CreatingListener)
+ {
+ throw new InvalidOperationException(Resources.GetResourceString("EventSource_ListenerCreatedInsideCallback"));
+ }
+
+ try
+ {
+ s_CreatingListener = true;
+
+ if (addToListenersList)
+ {
+ // Add to list of listeners in the system, do this BEFORE firing the 'OnEventSourceCreated' so that
+ // Those added sources see this listener.
+ this.m_Next = s_Listeners;
+ s_Listeners = this;
+ }
+
+ // Find all existing eventSources call OnEventSourceCreated to 'catchup'
+ // Note that we DO have reentrancy here because 'AddListener' calls out to user code (via OnEventSourceCreated callback)
+ // We tolerate this by iterating over a copy of the list here. New event sources will take care of adding listeners themselves
+ // EventSources are not guaranteed to be added at the end of the s_EventSource list -- We re-use slots when a new source
+ // is created.
+ WeakReference[] eventSourcesSnapshot = s_EventSources.ToArray();
+
+ for (int i = 0; i < eventSourcesSnapshot.Length; i++)
+ {
+ WeakReference eventSourceRef = eventSourcesSnapshot[i];
+ EventSource eventSource = eventSourceRef.Target as EventSource;
+ if (eventSource != null)
+ {
+ EventSourceCreatedEventArgs args = new EventSourceCreatedEventArgs();
+ args.EventSource = eventSource;
+ callback(this, args);
+ }
+ }
+
+ Validate();
+ }
+ finally
+ {
+ s_CreatingListener = false;
+ }
+ }
+
+ }
+
+ // Instance fields
+ internal volatile EventListener m_Next; // These form a linked list in s_Listeners
+#if FEATURE_ACTIVITYSAMPLING
+ internal ActivityFilter m_activityFilter; // If we are filtering by activity on this Listener, this keeps track of it.
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // static fields
+
+ /// <summary>
+ /// The list of all listeners in the appdomain. Listeners must be explicitly disposed to remove themselves
+ /// from this list. Note that EventSources point to their listener but NOT the reverse.
+ /// </summary>
+ internal static EventListener s_Listeners;
+ /// <summary>
+ /// The list of all active eventSources in the appdomain. Note that eventSources do NOT
+ /// remove themselves from this list this is a weak list and the GC that removes them may
+ /// not have happened yet. Thus it can contain event sources that are dead (thus you have
+ /// to filter those out.
+ /// </summary>
+ internal static List<WeakReference> s_EventSources;
+
+ /// <summary>
+ /// Used to disallow reentrancy.
+ /// </summary>
+ private static bool s_CreatingListener = false;
+
+ /// <summary>
+ /// Used to register AD/Process shutdown callbacks.
+ /// </summary>
+ private static bool s_EventSourceShutdownRegistered = false;
+ #endregion
+ }
+
+ /// <summary>
+ /// Passed to the code:EventSource.OnEventCommand callback
+ /// </summary>
+ public class EventCommandEventArgs : EventArgs
+ {
+ /// <summary>
+ /// Gets the command for the callback.
+ /// </summary>
+ public EventCommand Command { get; internal set; }
+
+ /// <summary>
+ /// Gets the arguments for the callback.
+ /// </summary>
+ public IDictionary<String, String> Arguments { get; internal set; }
+
+ /// <summary>
+ /// Enables the event that has the specified identifier.
+ /// </summary>
+ /// <param name="eventId">Event ID of event to be enabled</param>
+ /// <returns>true if eventId is in range</returns>
+ public bool EnableEvent(int eventId)
+ {
+ if (Command != EventCommand.Enable && Command != EventCommand.Disable)
+ throw new InvalidOperationException();
+ return eventSource.EnableEventForDispatcher(dispatcher, eventId, true);
+ }
+
+ /// <summary>
+ /// Disables the event that have the specified identifier.
+ /// </summary>
+ /// <param name="eventId">Event ID of event to be disabled</param>
+ /// <returns>true if eventId is in range</returns>
+ public bool DisableEvent(int eventId)
+ {
+ if (Command != EventCommand.Enable && Command != EventCommand.Disable)
+ throw new InvalidOperationException();
+ return eventSource.EnableEventForDispatcher(dispatcher, eventId, false);
+ }
+
+ #region private
+
+ internal EventCommandEventArgs(EventCommand command, IDictionary<string, string> arguments, EventSource eventSource,
+ EventListener listener, int perEventSourceSessionId, int etwSessionId, bool enable, EventLevel level, EventKeywords matchAnyKeyword)
+ {
+ this.Command = command;
+ this.Arguments = arguments;
+ this.eventSource = eventSource;
+ this.listener = listener;
+ this.perEventSourceSessionId = perEventSourceSessionId;
+ this.etwSessionId = etwSessionId;
+ this.enable = enable;
+ this.level = level;
+ this.matchAnyKeyword = matchAnyKeyword;
+ }
+
+ internal EventSource eventSource;
+ internal EventDispatcher dispatcher;
+
+ // These are the arguments of sendCommand and are only used for deferring commands until after we are fully initialized.
+ internal EventListener listener;
+ internal int perEventSourceSessionId;
+ internal int etwSessionId;
+ internal bool enable;
+ internal EventLevel level;
+ internal EventKeywords matchAnyKeyword;
+ internal EventCommandEventArgs nextCommand; // We form a linked list of these deferred commands.
+
+ #endregion
+ }
+
+ /// <summary>
+ /// EventSourceCreatedEventArgs is passed to <see cref="EventListener.EventSourceCreated"/>
+ /// </summary>
+ public class EventSourceCreatedEventArgs : EventArgs
+ {
+ /// <summary>
+ /// The EventSource that is attaching to the listener.
+ /// </summary>
+ public EventSource EventSource
+ {
+ get;
+ internal set;
+ }
+ }
+
+ /// <summary>
+ /// EventWrittenEventArgs is passed to the user-provided override for
+ /// <see cref="EventListener.OnEventWritten"/> when an event is fired.
+ /// </summary>
+ public class EventWrittenEventArgs : EventArgs
+ {
+ /// <summary>
+ /// The name of the event.
+ /// </summary>
+ public string EventName
+ {
+ get
+ {
+ if (m_eventName != null || EventId < 0) // TraceLogging convention EventID == -1
+ {
+ return m_eventName;
+ }
+ else
+ return m_eventSource.m_eventData[EventId].Name;
+ }
+ internal set
+ {
+ m_eventName = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the event ID for the event that was written.
+ /// </summary>
+ public int EventId { get; internal set; }
+
+ /// <summary>
+ /// Gets the activity ID for the thread on which the event was written.
+ /// </summary>
+ public Guid ActivityId
+ {
+ get { return EventSource.CurrentThreadActivityId; }
+ }
+
+ /// <summary>
+ /// Gets the related activity ID if one was specified when the event was written.
+ /// </summary>
+ public Guid RelatedActivityId
+ {
+ get;
+ internal set;
+ }
+
+ /// <summary>
+ /// Gets the payload for the event.
+ /// </summary>
+ public ReadOnlyCollection<Object> Payload { get; internal set; }
+
+ /// <summary>
+ /// Gets the payload argument names.
+ /// </summary>
+ public ReadOnlyCollection<string> PayloadNames
+ {
+ get
+ {
+ // For contract based events we create the list lazily.
+ if (m_payloadNames == null)
+ {
+ // Self described events are identified by id -1.
+ Debug.Assert(EventId != -1);
+
+ var names = new List<string>();
+ foreach (var parameter in m_eventSource.m_eventData[EventId].Parameters)
+ {
+ names.Add(parameter.Name);
+ }
+ m_payloadNames = new ReadOnlyCollection<string>(names);
+ }
+
+ return m_payloadNames;
+ }
+
+ internal set
+ {
+ m_payloadNames = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the event source object.
+ /// </summary>
+ public EventSource EventSource { get { return m_eventSource; } }
+
+ /// <summary>
+ /// Gets the keywords for the event.
+ /// </summary>
+ public EventKeywords Keywords
+ {
+ get
+ {
+ if (EventId < 0) // TraceLogging convention EventID == -1
+ return m_keywords;
+
+ return (EventKeywords)m_eventSource.m_eventData[EventId].Descriptor.Keywords;
+ }
+ }
+
+ /// <summary>
+ /// Gets the operation code for the event.
+ /// </summary>
+ public EventOpcode Opcode
+ {
+ get
+ {
+ if (EventId <= 0) // TraceLogging convention EventID == -1
+ return m_opcode;
+ return (EventOpcode)m_eventSource.m_eventData[EventId].Descriptor.Opcode;
+ }
+ }
+
+ /// <summary>
+ /// Gets the task for the event.
+ /// </summary>
+ public EventTask Task
+ {
+ get
+ {
+ if (EventId <= 0) // TraceLogging convention EventID == -1
+ return EventTask.None;
+
+ return (EventTask)m_eventSource.m_eventData[EventId].Descriptor.Task;
+ }
+ }
+
+ /// <summary>
+ /// Any provider/user defined options associated with the event.
+ /// </summary>
+ public EventTags Tags
+ {
+ get
+ {
+ if (EventId <= 0) // TraceLogging convention EventID == -1
+ return m_tags;
+ return m_eventSource.m_eventData[EventId].Tags;
+ }
+ }
+
+ /// <summary>
+ /// Gets the message for the event. If the message has {N} parameters they are NOT substituted.
+ /// </summary>
+ public string Message
+ {
+ get
+ {
+ if (EventId <= 0) // TraceLogging convention EventID == -1
+ return m_message;
+ else
+ return m_eventSource.m_eventData[EventId].Message;
+ }
+ internal set
+ {
+ m_message = value;
+ }
+ }
+
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// Gets the channel for the event.
+ /// </summary>
+ public EventChannel Channel
+ {
+ get
+ {
+ if (EventId <= 0) // TraceLogging convention EventID == -1
+ return EventChannel.None;
+ return (EventChannel)m_eventSource.m_eventData[EventId].Descriptor.Channel;
+ }
+ }
+#endif
+
+ /// <summary>
+ /// Gets the version of the event.
+ /// </summary>
+ public byte Version
+ {
+ get
+ {
+ if (EventId <= 0) // TraceLogging convention EventID == -1
+ return 0;
+ return m_eventSource.m_eventData[EventId].Descriptor.Version;
+ }
+ }
+
+ /// <summary>
+ /// Gets the level for the event.
+ /// </summary>
+ public EventLevel Level
+ {
+ get
+ {
+ if (EventId <= 0) // TraceLogging convention EventID == -1
+ return m_level;
+ return (EventLevel)m_eventSource.m_eventData[EventId].Descriptor.Level;
+ }
+ }
+
+ #region private
+ internal EventWrittenEventArgs(EventSource eventSource)
+ {
+ m_eventSource = eventSource;
+ }
+ private string m_message;
+ private string m_eventName;
+ private EventSource m_eventSource;
+ private ReadOnlyCollection<string> m_payloadNames;
+ internal EventTags m_tags;
+ internal EventOpcode m_opcode;
+ internal EventLevel m_level;
+ internal EventKeywords m_keywords;
+ #endregion
+ }
+
+ /// <summary>
+ /// Allows customizing defaults and specifying localization support for the event source class to which it is applied.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class)]
+ public sealed class EventSourceAttribute : Attribute
+ {
+ /// <summary>
+ /// Overrides the ETW name of the event source (which defaults to the class name)
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Overrides the default (calculated) Guid of an EventSource type. Explicitly defining a GUID is discouraged,
+ /// except when upgrading existing ETW providers to using event sources.
+ /// </summary>
+ public string Guid { get; set; }
+
+ /// <summary>
+ /// <para>
+ /// EventSources support localization of events. The names used for events, opcodes, tasks, keywords and maps
+ /// can be localized to several languages if desired. This works by creating a ResX style string table
+ /// (by simply adding a 'Resource File' to your project). This resource file is given a name e.g.
+ /// 'DefaultNameSpace.ResourceFileName' which can be passed to the ResourceManager constructor to read the
+ /// resources. This name is the value of the LocalizationResources property.
+ /// </para><para>
+ /// If LocalizationResources property is non-null, then EventSource will look up the localized strings for events by
+ /// using the following resource naming scheme
+ /// </para>
+ /// <para>* event_EVENTNAME</para>
+ /// <para>* task_TASKNAME</para>
+ /// <para>* keyword_KEYWORDNAME</para>
+ /// <para>* map_MAPNAME</para>
+ /// <para>
+ /// where the capitalized name is the name of the event, task, keyword, or map value that should be localized.
+ /// Note that the localized string for an event corresponds to the Message string, and can have {0} values
+ /// which represent the payload values.
+ /// </para>
+ /// </summary>
+ public string LocalizationResources { get; set; }
+ }
+
+ /// <summary>
+ /// Any instance methods in a class that subclasses <see cref="EventSource"/> and that return void are
+ /// assumed by default to be methods that generate an ETW event. Enough information can be deduced from the
+ /// name of the method and its signature to generate basic schema information for the event. The
+ /// <see cref="EventAttribute"/> class allows you to specify additional event schema information for an event if
+ /// desired.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method)]
+ public sealed class EventAttribute : Attribute
+ {
+ /// <summary>Construct an EventAttribute with specified eventId</summary>
+ /// <param name="eventId">ID of the ETW event (an integer between 1 and 65535)</param>
+ public EventAttribute(int eventId) { this.EventId = eventId; Level = EventLevel.Informational; this.m_opcodeSet = false; }
+ /// <summary>Event's ID</summary>
+ public int EventId { get; private set; }
+ /// <summary>Event's severity level: indicates the severity or verbosity of the event</summary>
+ public EventLevel Level { get; set; }
+ /// <summary>Event's keywords: allows classification of events by "categories"</summary>
+ public EventKeywords Keywords { get; set; }
+ /// <summary>Event's operation code: allows defining operations, generally used with Tasks</summary>
+ public EventOpcode Opcode
+ {
+ get
+ {
+ return m_opcode;
+ }
+ set
+ {
+ this.m_opcode = value;
+ this.m_opcodeSet = true;
+ }
+ }
+
+ internal bool IsOpcodeSet
+ {
+ get
+ {
+ return m_opcodeSet;
+ }
+ }
+
+ /// <summary>Event's task: allows logical grouping of events</summary>
+ public EventTask Task { get; set; }
+#if FEATURE_MANAGED_ETW_CHANNELS
+ /// <summary>Event's channel: defines an event log as an additional destination for the event</summary>
+ public EventChannel Channel { get; set; }
+#endif
+ /// <summary>Event's version</summary>
+ public byte Version { get; set; }
+
+ /// <summary>
+ /// This can be specified to enable formatting and localization of the event's payload. You can
+ /// use standard .NET substitution operators (eg {1}) in the string and they will be replaced
+ /// with the 'ToString()' of the corresponding part of the event payload.
+ /// </summary>
+ public string Message { get; set; }
+
+ /// <summary>
+ /// User defined options associated with the event. These do not have meaning to the EventSource but
+ /// are passed through to listeners which given them semantics.
+ /// </summary>
+ public EventTags Tags { get; set; }
+
+ /// <summary>
+ /// Allows fine control over the Activity IDs generated by start and stop events
+ /// </summary>
+ public EventActivityOptions ActivityOptions { get; set; }
+
+ #region private
+ EventOpcode m_opcode;
+ private bool m_opcodeSet;
+ #endregion
+ }
+
+ /// <summary>
+ /// By default all instance methods in a class that subclasses code:EventSource that and return
+ /// void are assumed to be methods that generate an event. This default can be overridden by specifying
+ /// the code:NonEventAttribute
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method)]
+ public sealed class NonEventAttribute : Attribute
+ {
+ /// <summary>
+ /// Constructs a default NonEventAttribute
+ /// </summary>
+ public NonEventAttribute() { }
+ }
+
+ // FUTURE we may want to expose this at some point once we have a partner that can help us validate the design.
+#if FEATURE_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// EventChannelAttribute allows customizing channels supported by an EventSource. This attribute must be
+ /// applied to an member of type EventChannel defined in a Channels class nested in the EventSource class:
+ /// <code>
+ /// public static class Channels
+ /// {
+ /// [Channel(Enabled = true, EventChannelType = EventChannelType.Admin)]
+ /// public const EventChannel Admin = (EventChannel)16;
+ ///
+ /// [Channel(Enabled = false, EventChannelType = EventChannelType.Operational)]
+ /// public const EventChannel Operational = (EventChannel)17;
+ /// }
+ /// </code>
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Field)]
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ public
+#endif
+ class EventChannelAttribute : Attribute
+ {
+ /// <summary>
+ /// Specified whether the channel is enabled by default
+ /// </summary>
+ public bool Enabled { get; set; }
+
+ /// <summary>
+ /// Legal values are in EventChannelType
+ /// </summary>
+ public EventChannelType EventChannelType { get; set; }
+
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// Specifies the isolation for the channel
+ /// </summary>
+ public EventChannelIsolation Isolation { get; set; }
+
+ /// <summary>
+ /// Specifies an SDDL access descriptor that controls access to the log file that backs the channel.
+ /// See MSDN ((http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx) for details.
+ /// </summary>
+ public string Access { get; set; }
+
+ /// <summary>
+ /// Allows importing channels defined in external manifests
+ /// </summary>
+ public string ImportChannel { get; set; }
+#endif
+
+ // TODO: there is a convention that the name is the Provider/Type Should we provide an override?
+ // public string Name { get; set; }
+ }
+
+ /// <summary>
+ /// Allowed channel types
+ /// </summary>
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ public
+#endif
+ enum EventChannelType
+ {
+ /// <summary>The admin channel</summary>
+ Admin = 1,
+ /// <summary>The operational channel</summary>
+ Operational,
+ /// <summary>The Analytic channel</summary>
+ Analytic,
+ /// <summary>The debug channel</summary>
+ Debug,
+ }
+
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// Allowed isolation levels. See MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/aa382741.aspx)
+ /// for the default permissions associated with each level. EventChannelIsolation and Access allows control over the
+ /// access permissions for the channel and backing file.
+ /// </summary>
+ public
+ enum EventChannelIsolation
+ {
+ /// <summary>
+ /// This is the default isolation level. All channels that specify Application isolation use the same ETW session
+ /// </summary>
+ Application = 1,
+ /// <summary>
+ /// All channels that specify System isolation use the same ETW session
+ /// </summary>
+ System,
+ /// <summary>
+ /// Use sparingly! When specifying Custom isolation, a separate ETW session is created for the channel.
+ /// Using Custom isolation lets you control the access permissions for the channel and backing file.
+ /// Because there are only 64 ETW sessions available, you should limit your use of Custom isolation.
+ /// </summary>
+ Custom,
+ }
+#endif
+#endif
+
+ /// <summary>
+ /// Describes the pre-defined command (EventCommandEventArgs.Command property) that is passed to the OnEventCommand callback.
+ /// </summary>
+ public enum EventCommand
+ {
+ /// <summary>
+ /// Update EventSource state
+ /// </summary>
+ Update = 0,
+ /// <summary>
+ /// Request EventSource to generate and send its manifest
+ /// </summary>
+ SendManifest = -1,
+ /// <summary>
+ /// Enable event
+ /// </summary>
+ Enable = -2,
+ /// <summary>
+ /// Disable event
+ /// </summary>
+ Disable = -3
+ };
+
+
+ #region private classes
+
+#if FEATURE_ACTIVITYSAMPLING
+
+ /// <summary>
+ /// ActivityFilter is a helper structure that is used to keep track of run-time state
+ /// associated with activity filtering. It is 1-1 with EventListeners (logically
+ /// every listener has one of these, however we actually allocate them lazily), as well
+ /// as 1-to-1 with tracing-aware EtwSessions.
+ ///
+ /// This structure also keeps track of the sampling counts associated with 'trigger'
+ /// events. Because these trigger events are rare, and you typically only have one of
+ /// them, we store them here as a linked list.
+ /// </summary>
+ internal sealed class ActivityFilter : IDisposable
+ {
+ /// <summary>
+ /// Disable all activity filtering for the listener associated with 'filterList',
+ /// (in the session associated with it) that is triggered by any event in 'source'.
+ /// </summary>
+ public static void DisableFilter(ref ActivityFilter filterList, EventSource source)
+ {
+#if !ES_BUILD_STANDALONE
+ Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+
+ if (filterList == null)
+ return;
+
+ ActivityFilter cur;
+ // Remove it from anywhere in the list (except the first element, which has to
+ // be treated specially)
+ ActivityFilter prev = filterList;
+ cur = prev.m_next;
+ while (cur != null)
+ {
+ if (cur.m_providerGuid == source.Guid)
+ {
+ // update TriggersActivityTracking bit
+ if (cur.m_eventId >= 0 && cur.m_eventId < source.m_eventData.Length)
+ --source.m_eventData[cur.m_eventId].TriggersActivityTracking;
+
+ // Remove it from the linked list.
+ prev.m_next = cur.m_next;
+ // dispose of the removed node
+ cur.Dispose();
+ // update cursor
+ cur = prev.m_next;
+ }
+ else
+ {
+ // update cursors
+ prev = cur;
+ cur = prev.m_next;
+ }
+ }
+
+ // Sadly we have to treat the first element specially in linked list removal in C#
+ if (filterList.m_providerGuid == source.Guid)
+ {
+ // update TriggersActivityTracking bit
+ if (filterList.m_eventId >= 0 && filterList.m_eventId < source.m_eventData.Length)
+ --source.m_eventData[filterList.m_eventId].TriggersActivityTracking;
+
+ // We are the first element in the list.
+ var first = filterList;
+ filterList = first.m_next;
+ // dispose of the removed node
+ first.Dispose();
+ }
+ // the above might have removed the one ActivityFilter in the session that contains the
+ // cleanup delegate; re-create the delegate if needed
+ if (filterList != null)
+ {
+ EnsureActivityCleanupDelegate(filterList);
+ }
+ }
+
+ /// <summary>
+ /// Currently this has "override" semantics. We first disable all filters
+ /// associated with 'source', and next we add new filters for each entry in the
+ /// string 'startEvents'. participateInSampling specifies whether non-startEvents
+ /// always trigger or only trigger when current activity is 'active'.
+ /// </summary>
+ public static void UpdateFilter(
+ ref ActivityFilter filterList,
+ EventSource source,
+ int perEventSourceSessionId,
+ string startEvents)
+ {
+#if !ES_BUILD_STANDALONE
+ Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+
+ // first remove all filters associated with 'source'
+ DisableFilter(ref filterList, source);
+
+ if (!string.IsNullOrEmpty(startEvents))
+ {
+ // ActivitySamplingStartEvents is a space-separated list of Event:Frequency pairs.
+ // The Event may be specified by name or by ID. Errors in parsing such a pair
+ // result in the error being reported to the listeners, and the pair being ignored.
+ // E.g. "CustomActivityStart:1000 12:10" specifies that for event CustomActivityStart
+ // we should initiate activity tracing once every 1000 events, *and* for event ID 12
+ // we should initiate activity tracing once every 10 events.
+ string[] activityFilterStrings = startEvents.Split(' ');
+
+ for (int i = 0; i < activityFilterStrings.Length; ++i)
+ {
+ string activityFilterString = activityFilterStrings[i];
+ int sampleFreq = 1;
+ int eventId = -1;
+ int colonIdx = activityFilterString.IndexOf(':');
+ if (colonIdx < 0)
+ {
+ source.ReportOutOfBandMessage("ERROR: Invalid ActivitySamplingStartEvent specification: " +
+ activityFilterString, false);
+ // ignore failure...
+ continue;
+ }
+ string sFreq = activityFilterString.Substring(colonIdx + 1);
+ if (!int.TryParse(sFreq, out sampleFreq))
+ {
+ source.ReportOutOfBandMessage("ERROR: Invalid sampling frequency specification: " + sFreq, false);
+ continue;
+ }
+ activityFilterString = activityFilterString.Substring(0, colonIdx);
+ if (!int.TryParse(activityFilterString, out eventId))
+ {
+ // reset eventId
+ eventId = -1;
+ // see if it's an event name
+ for (int j = 0; j < source.m_eventData.Length; j++)
+ {
+ EventSource.EventMetadata[] ed = source.m_eventData;
+ if (ed[j].Name != null && ed[j].Name.Length == activityFilterString.Length &&
+ string.Compare(ed[j].Name, activityFilterString, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ eventId = ed[j].Descriptor.EventId;
+ break;
+ }
+ }
+ }
+ if (eventId < 0 || eventId >= source.m_eventData.Length)
+ {
+ source.ReportOutOfBandMessage("ERROR: Invalid eventId specification: " + activityFilterString, false);
+ continue;
+ }
+ EnableFilter(ref filterList, source, perEventSourceSessionId, eventId, sampleFreq);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns the first ActivityFilter from 'filterList' corresponding to 'source'.
+ /// </summary>
+ public static ActivityFilter GetFilter(ActivityFilter filterList, EventSource source)
+ {
+ for (var af = filterList; af != null; af = af.m_next)
+ {
+ if (af.m_providerGuid == source.Guid && af.m_samplingFreq != -1)
+ return af;
+ }
+ return null;
+ }
+
+ /// <summary>
+ /// Returns a session mask representing all sessions in which the activity
+ /// associated with the current thread is allowed through the activity filter.
+ /// If 'triggeringEvent' is true the event MAY be a triggering event. Ideally
+ /// most of the time this is false as you can guarentee this event is NOT a
+ /// triggering event. If 'triggeringEvent' is true, then it checks the
+ /// 'EventSource' and 'eventID' of the event being logged to see if it is actually
+ /// a trigger. If so it activates the current activity.
+ ///
+ /// If 'childActivityID' is present, it will be added to the active set if the
+ /// current activity is active.
+ /// </summary>
+ unsafe public static bool PassesActivityFilter(
+ ActivityFilter filterList,
+ Guid* childActivityID,
+ bool triggeringEvent,
+ EventSource source,
+ int eventId)
+ {
+ Debug.Assert(filterList != null && filterList.m_activeActivities != null);
+ bool shouldBeLogged = false;
+ if (triggeringEvent)
+ {
+ for (ActivityFilter af = filterList; af != null; af = af.m_next)
+ {
+ if (eventId == af.m_eventId && source.Guid == af.m_providerGuid)
+ {
+ // Update the sampling count with wrap-around
+ int curSampleCount, newSampleCount;
+ do
+ {
+ curSampleCount = af.m_curSampleCount;
+ if (curSampleCount <= 1)
+ newSampleCount = af.m_samplingFreq; // Wrap around, counting down to 1
+ else
+ newSampleCount = curSampleCount - 1;
+ }
+ while (Interlocked.CompareExchange(ref af.m_curSampleCount, newSampleCount, curSampleCount) != curSampleCount);
+ // If we hit zero, then start tracking the activity.
+ if (curSampleCount <= 1)
+ {
+ Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
+ Tuple<Guid, int> startId;
+ // only add current activity if it's not already a root activity
+ if (!af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId))
+ {
+ // EventSource.OutputDebugString(string.Format(" PassesAF - Triggering(session {0}, evt {1})", af.m_perEventSourceSessionId, eventId));
+ shouldBeLogged = true;
+ af.m_activeActivities[currentActivityId] = Environment.TickCount;
+ af.m_rootActiveActivities[currentActivityId] = Tuple.Create(source.Guid, eventId);
+ }
+ }
+ else
+ {
+ // a start event following a triggering start event
+ Guid currentActivityId = EventSource.InternalCurrentThreadActivityId;
+ Tuple<Guid, int> startId;
+ // only remove current activity if we added it
+ if (af.m_rootActiveActivities.TryGetValue(currentActivityId, out startId) &&
+ startId.Item1 == source.Guid && startId.Item2 == eventId)
+ {
+ // EventSource.OutputDebugString(string.Format("Activity dying: {0} -> StartEvent({1})", currentActivityId, eventId));
+ // remove activity only from current logging scope (af)
+ int dummy;
+ af.m_activeActivities.TryRemove(currentActivityId, out dummy);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ var activeActivities = GetActiveActivities(filterList);
+ if (activeActivities != null)
+ {
+ // if we hadn't already determined this should be logged, test further
+ if (!shouldBeLogged)
+ {
+ shouldBeLogged = !activeActivities.IsEmpty &&
+ activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId);
+ }
+ if (shouldBeLogged && childActivityID != null &&
+ ((EventOpcode)source.m_eventData[eventId].Descriptor.Opcode == EventOpcode.Send))
+ {
+ FlowActivityIfNeeded(filterList, null, childActivityID);
+ // EventSource.OutputDebugString(string.Format(" PassesAF - activity {0}", *childActivityID));
+ }
+ }
+ // EventSource.OutputDebugString(string.Format(" PassesAF - shouldBeLogged(evt {0}) = {1:x}", eventId, shouldBeLogged));
+ return shouldBeLogged;
+ }
+
+ public static bool IsCurrentActivityActive(ActivityFilter filterList)
+ {
+ var activeActivities = GetActiveActivities(filterList);
+ if (activeActivities != null &&
+ activeActivities.ContainsKey(EventSource.InternalCurrentThreadActivityId))
+ return true;
+
+ return false;
+ }
+
+ /// <summary>
+ /// For the EventListener/EtwSession associated with 'filterList', add 'childActivityid'
+ /// to list of active activities IF 'currentActivityId' is also active. Passing in a null
+ /// value for 'currentActivityid' is an indication tha caller has already verified
+ /// that the current activity is active.
+ /// </summary>
+ unsafe public static void FlowActivityIfNeeded(ActivityFilter filterList, Guid* currentActivityId, Guid* childActivityID)
+ {
+ Debug.Assert(childActivityID != null);
+
+ var activeActivities = GetActiveActivities(filterList);
+ Debug.Assert(activeActivities != null);
+
+ // take currentActivityId == null to mean we *know* the current activity is "active"
+ if (currentActivityId != null && !activeActivities.ContainsKey(*currentActivityId))
+ return;
+
+ if (activeActivities.Count > MaxActivityTrackCount)
+ {
+ TrimActiveActivityStore(activeActivities);
+ // make sure current activity is still in the set:
+ activeActivities[EventSource.InternalCurrentThreadActivityId] = Environment.TickCount;
+ }
+ // add child activity to list of actives
+ activeActivities[*childActivityID] = Environment.TickCount;
+
+ }
+
+ /// <summary>
+ /// </summary>
+ public static void UpdateKwdTriggers(ActivityFilter activityFilter, Guid sourceGuid, EventSource source, EventKeywords sessKeywords)
+ {
+ for (var af = activityFilter; af != null; af = af.m_next)
+ {
+ if ((sourceGuid == af.m_providerGuid) &&
+ (source.m_eventData[af.m_eventId].TriggersActivityTracking > 0 ||
+ ((EventOpcode)source.m_eventData[af.m_eventId].Descriptor.Opcode == EventOpcode.Send)))
+ {
+ // we could be more precise here, if we tracked 'anykeywords' per session
+ unchecked
+ {
+ source.m_keywordTriggers |= (source.m_eventData[af.m_eventId].Descriptor.Keywords & (long)sessKeywords);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// For the EventSource specified by 'sourceGuid' and the EventListener/EtwSession
+ /// associated with 'this' ActivityFilter list, return configured sequence of
+ /// [eventId, sampleFreq] pairs that defines the sampling policy.
+ /// </summary>
+ public IEnumerable<Tuple<int, int>> GetFilterAsTuple(Guid sourceGuid)
+ {
+ for (ActivityFilter af = this; af != null; af = af.m_next)
+ {
+ if (af.m_providerGuid == sourceGuid)
+ yield return Tuple.Create(af.m_eventId, af.m_samplingFreq);
+ }
+ }
+
+ /// <summary>
+ /// The cleanup being performed consists of removing the m_myActivityDelegate from
+ /// the static s_activityDying, therefore allowing the ActivityFilter to be reclaimed.
+ /// </summary>
+ public void Dispose()
+ {
+#if !ES_BUILD_STANDALONE
+ Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+ // m_myActivityDelegate is still alive (held by the static EventSource.s_activityDying).
+ // Therefore we are ok to take a dependency on m_myActivityDelegate being valid even
+ // during the finalization of the ActivityFilter
+ if (m_myActivityDelegate != null)
+ {
+ EventSource.s_activityDying = (Action<Guid>)Delegate.Remove(EventSource.s_activityDying, m_myActivityDelegate);
+ m_myActivityDelegate = null;
+ }
+ }
+
+ #region private
+
+ /// <summary>
+ /// Creates a new ActivityFilter that is triggered by 'eventId' from 'source' ever
+ /// 'samplingFreq' times the event fires. You can have several of these forming a
+ /// linked list.
+ /// </summary>
+ private ActivityFilter(EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq, ActivityFilter existingFilter = null)
+ {
+ m_providerGuid = source.Guid;
+ m_perEventSourceSessionId = perEventSourceSessionId;
+ m_eventId = eventId;
+ m_samplingFreq = samplingFreq;
+ m_next = existingFilter;
+
+ Debug.Assert(existingFilter == null ||
+ (existingFilter.m_activeActivities == null) == (existingFilter.m_rootActiveActivities == null));
+
+ // if this is the first filter we add for this session, we need to create a new
+ // table of activities. m_activeActivities is common across EventSources in the same
+ // session
+ ConcurrentDictionary<Guid, int> activeActivities = null;
+ if (existingFilter == null ||
+ (activeActivities = GetActiveActivities(existingFilter)) == null)
+ {
+ m_activeActivities = new ConcurrentDictionary<Guid, int>();
+ m_rootActiveActivities = new ConcurrentDictionary<Guid, Tuple<Guid, int>>();
+
+ // Add a delegate to the 'SetCurrentThreadToActivity callback so that I remove 'dead' activities
+ m_myActivityDelegate = GetActivityDyingDelegate(this);
+ EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, m_myActivityDelegate);
+ }
+ else
+ {
+ m_activeActivities = activeActivities;
+ m_rootActiveActivities = existingFilter.m_rootActiveActivities;
+ }
+
+ }
+
+ /// <summary>
+ /// Ensure there's at least one ActivityFilter in the 'filterList' that contains an
+ /// activity-removing delegate for the listener/session associated with 'filterList'.
+ /// </summary>
+ private static void EnsureActivityCleanupDelegate(ActivityFilter filterList)
+ {
+ if (filterList == null)
+ return;
+
+ for (ActivityFilter af = filterList; af != null; af = af.m_next)
+ {
+ if (af.m_myActivityDelegate != null)
+ return;
+ }
+
+ // we didn't find a delegate
+ filterList.m_myActivityDelegate = GetActivityDyingDelegate(filterList);
+ EventSource.s_activityDying = (Action<Guid>)Delegate.Combine(EventSource.s_activityDying, filterList.m_myActivityDelegate);
+ }
+
+ /// <summary>
+ /// Builds the delegate to be called when an activity is dying. This is responsible
+ /// for performing whatever cleanup is needed for the ActivityFilter list passed in.
+ /// This gets "added" to EventSource.s_activityDying and ends up being called from
+ /// EventSource.SetCurrentThreadActivityId and ActivityFilter.PassesActivityFilter.
+ /// </summary>
+ /// <returns>The delegate to be called when an activity is dying</returns>
+ private static Action<Guid> GetActivityDyingDelegate(ActivityFilter filterList)
+ {
+ return (Guid oldActivity) =>
+ {
+ int dummy;
+ filterList.m_activeActivities.TryRemove(oldActivity, out dummy);
+ Tuple<Guid, int> dummyTuple;
+ filterList.m_rootActiveActivities.TryRemove(oldActivity, out dummyTuple);
+ };
+ }
+
+ /// <summary>
+ /// Enables activity filtering for the listener associated with 'filterList', triggering on
+ /// the event 'eventID' from 'source' with a sampling frequency of 'samplingFreq'
+ ///
+ /// if 'eventID' is out of range (e.g. negative), it means we are not triggering (but we are
+ /// activitySampling if something else triggered).
+ /// </summary>
+ /// <returns>true if activity sampling is enabled the samplingFreq is non-zero </returns>
+ private static bool EnableFilter(ref ActivityFilter filterList, EventSource source, int perEventSourceSessionId, int eventId, int samplingFreq)
+ {
+#if !ES_BUILD_STANDALONE
+ Debug.Assert(Monitor.IsEntered(EventListener.EventListenersLock));
+#endif
+ Debug.Assert(samplingFreq > 0);
+ Debug.Assert(eventId >= 0);
+
+ filterList = new ActivityFilter(source, perEventSourceSessionId, eventId, samplingFreq, filterList);
+
+ // Mark the 'quick Check' that indicates this is a trigger event.
+ // If eventId is out of range then this mark is not done which has the effect of ignoring
+ // the trigger.
+ if (0 <= eventId && eventId < source.m_eventData.Length)
+ ++source.m_eventData[eventId].TriggersActivityTracking;
+
+ return true;
+ }
+
+ /// <summary>
+ /// Normally this code never runs, it is here just to prevent run-away resource usage.
+ /// </summary>
+ private static void TrimActiveActivityStore(ConcurrentDictionary<Guid, int> activities)
+ {
+ if (activities.Count > MaxActivityTrackCount)
+ {
+ // Remove half of the oldest activity ids.
+ var keyValues = activities.ToArray();
+ var tickNow = Environment.TickCount;
+
+ // Sort by age, taking into account wrap-around. As long as x and y are within
+ // 23 days of now then (0x7FFFFFFF & (tickNow - x.Value)) is the delta (even if
+ // TickCount wraps). I then sort by DESCENDING age. (that is oldest value first)
+ Array.Sort(keyValues, (x, y) => (0x7FFFFFFF & (tickNow - y.Value)) - (0x7FFFFFFF & (tickNow - x.Value)));
+ for (int i = 0; i < keyValues.Length / 2; i++)
+ {
+ int dummy;
+ activities.TryRemove(keyValues[i].Key, out dummy);
+ }
+ }
+ }
+
+ private static ConcurrentDictionary<Guid, int> GetActiveActivities(
+ ActivityFilter filterList)
+ {
+ for (ActivityFilter af = filterList; af != null; af = af.m_next)
+ {
+ if (af.m_activeActivities != null)
+ return af.m_activeActivities;
+ }
+ return null;
+ }
+
+ // m_activeActivities always points to the sample dictionary for EVERY ActivityFilter
+ // in the m_next list. The 'int' value in the m_activities set is a timestamp
+ // (Environment.TickCount) of when the entry was put in the system and is used to
+ // remove 'old' entries that if the set gets too big.
+ ConcurrentDictionary<Guid, int> m_activeActivities;
+
+ // m_rootActiveActivities holds the "root" active activities, i.e. the activities
+ // that were marked as active because a Start event fired on them. We need to keep
+ // track of these to enable sampling in the scenario of an app's main thread that
+ // never explicitly sets distinct activity IDs as it executes. To handle these
+ // situations we manufacture a Guid from the thread's ID, and:
+ // (a) we consider the firing of a start event when the sampling counter reaches
+ // zero to mark the beginning of an interesting activity, and
+ // (b) we consider the very next firing of the same start event to mark the
+ // ending of that activity.
+ // We use a ConcurrentDictionary to avoid taking explicit locks.
+ // The key (a guid) represents the activity ID of the root active activity
+ // The value is made up of the Guid of the event provider and the eventId of
+ // the start event.
+ ConcurrentDictionary<Guid, Tuple<Guid, int>> m_rootActiveActivities;
+ Guid m_providerGuid; // We use the GUID rather than object identity because we don't want to keep the eventSource alive
+ int m_eventId; // triggering event
+ int m_samplingFreq; // Counter reset to this when it hits 0
+ int m_curSampleCount; // We count down to 0 and then activate the activity.
+ int m_perEventSourceSessionId; // session ID bit for ETW, 0 for EventListeners
+
+ const int MaxActivityTrackCount = 100000; // maximum number of tracked activities
+
+ ActivityFilter m_next; // We create a linked list of these
+ Action<Guid> m_myActivityDelegate;
+ #endregion
+ };
+
+
+ /// <summary>
+ /// An EtwSession instance represents an activity-tracing-aware ETW session. Since these
+ /// are limited to 8 concurrent sessions per machine (currently) we're going to store
+ /// the active ones in a singly linked list.
+ /// </summary>
+ internal class EtwSession
+ {
+ public static EtwSession GetEtwSession(int etwSessionId, bool bCreateIfNeeded = false)
+ {
+ if (etwSessionId < 0)
+ return null;
+
+ EtwSession etwSession;
+ foreach (var wrEtwSession in s_etwSessions)
+ {
+#if ES_BUILD_STANDALONE
+ if ((etwSession = (EtwSession) wrEtwSession.Target) != null && etwSession.m_etwSessionId == etwSessionId)
+ return etwSession;
+#else
+ if (wrEtwSession.TryGetTarget(out etwSession) && etwSession.m_etwSessionId == etwSessionId)
+ return etwSession;
+#endif
+ }
+
+ if (!bCreateIfNeeded)
+ return null;
+
+#if ES_BUILD_STANDALONE
+ if (s_etwSessions == null)
+ s_etwSessions = new List<WeakReference>();
+
+ etwSession = new EtwSession(etwSessionId);
+ s_etwSessions.Add(new WeakReference(etwSession));
+#else
+ if (s_etwSessions == null)
+ s_etwSessions = new List<WeakReference<EtwSession>>();
+
+ etwSession = new EtwSession(etwSessionId);
+ s_etwSessions.Add(new WeakReference<EtwSession>(etwSession));
+#endif
+
+ if (s_etwSessions.Count > s_thrSessionCount)
+ TrimGlobalList();
+
+ return etwSession;
+
+ }
+
+ public static void RemoveEtwSession(EtwSession etwSession)
+ {
+ Debug.Assert(etwSession != null);
+ if (s_etwSessions == null || etwSession == null)
+ return;
+
+ s_etwSessions.RemoveAll((wrEtwSession) =>
+ {
+ EtwSession session;
+#if ES_BUILD_STANDALONE
+ return (session = (EtwSession) wrEtwSession.Target) != null &&
+ (session.m_etwSessionId == etwSession.m_etwSessionId);
+#else
+ return wrEtwSession.TryGetTarget(out session) &&
+ (session.m_etwSessionId == etwSession.m_etwSessionId);
+#endif
+ });
+
+ if (s_etwSessions.Count > s_thrSessionCount)
+ TrimGlobalList();
+ }
+
+ private static void TrimGlobalList()
+ {
+ if (s_etwSessions == null)
+ return;
+
+ s_etwSessions.RemoveAll((wrEtwSession) =>
+ {
+#if ES_BUILD_STANDALONE
+ return wrEtwSession.Target == null;
+#else
+ EtwSession session;
+ return !wrEtwSession.TryGetTarget(out session);
+#endif
+ });
+ }
+
+ private EtwSession(int etwSessionId)
+ {
+ m_etwSessionId = etwSessionId;
+ }
+
+ public readonly int m_etwSessionId; // ETW session ID (as retrieved by EventProvider)
+ public ActivityFilter m_activityFilter; // all filters enabled for this session
+
+#if ES_BUILD_STANDALONE
+ private static List<WeakReference> s_etwSessions = new List<WeakReference>();
+#else
+ private static List<WeakReference<EtwSession>> s_etwSessions = new List<WeakReference<EtwSession>>();
+#endif
+ private const int s_thrSessionCount = 16;
+ }
+
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // holds a bitfield representing a session mask
+ /// <summary>
+ /// A SessionMask represents a set of (at most MAX) sessions as a bit mask. The perEventSourceSessionId
+ /// is the index in the SessionMask of the bit that will be set. These can translate to
+ /// EventSource's reserved keywords bits using the provided ToEventKeywords() and
+ /// FromEventKeywords() methods.
+ /// </summary>
+ internal struct SessionMask
+ {
+ public SessionMask(SessionMask m)
+ { m_mask = m.m_mask; }
+
+ public SessionMask(uint mask = 0)
+ { m_mask = mask & MASK; }
+
+ public bool IsEqualOrSupersetOf(SessionMask m)
+ {
+ return (this.m_mask | m.m_mask) == this.m_mask;
+ }
+
+ public static SessionMask All
+ {
+ get { return new SessionMask(MASK); }
+ }
+
+ public static SessionMask FromId(int perEventSourceSessionId)
+ {
+ Debug.Assert(perEventSourceSessionId < MAX);
+ return new SessionMask((uint)1 << perEventSourceSessionId);
+ }
+
+ public ulong ToEventKeywords()
+ {
+ return (ulong)m_mask << SHIFT_SESSION_TO_KEYWORD;
+ }
+
+ public static SessionMask FromEventKeywords(ulong m)
+ {
+ return new SessionMask((uint)(m >> SHIFT_SESSION_TO_KEYWORD));
+ }
+
+ public bool this[int perEventSourceSessionId]
+ {
+ get
+ {
+ Debug.Assert(perEventSourceSessionId < MAX);
+ return (m_mask & (1 << perEventSourceSessionId)) != 0;
+ }
+ set
+ {
+ Debug.Assert(perEventSourceSessionId < MAX);
+ if (value) m_mask |= ((uint)1 << perEventSourceSessionId);
+ else m_mask &= ~((uint)1 << perEventSourceSessionId);
+ }
+ }
+
+ public static SessionMask operator |(SessionMask m1, SessionMask m2)
+ {
+ return new SessionMask(m1.m_mask | m2.m_mask);
+ }
+
+ public static SessionMask operator &(SessionMask m1, SessionMask m2)
+ {
+ return new SessionMask(m1.m_mask & m2.m_mask);
+ }
+
+ public static SessionMask operator ^(SessionMask m1, SessionMask m2)
+ {
+ return new SessionMask(m1.m_mask ^ m2.m_mask);
+ }
+
+ public static SessionMask operator ~(SessionMask m)
+ {
+ return new SessionMask(MASK & ~(m.m_mask));
+ }
+
+ public static explicit operator ulong(SessionMask m)
+ { return m.m_mask; }
+
+ public static explicit operator uint(SessionMask m)
+ { return m.m_mask; }
+
+ private uint m_mask;
+
+ internal const int SHIFT_SESSION_TO_KEYWORD = 44; // bits 44-47 inclusive are reserved
+ internal const uint MASK = 0x0fU; // the mask of 4 reserved bits
+ internal const uint MAX = 4; // maximum number of simultaneous ETW sessions supported
+ }
+
+ /// <summary>
+ /// code:EventDispatchers are a simple 'helper' structure that holds the filtering state
+ /// (m_EventEnabled) for a particular EventSource X EventListener tuple
+ ///
+ /// Thus a single EventListener may have many EventDispatchers (one for every EventSource
+ /// that that EventListener has activate) and a Single EventSource may also have many
+ /// event Dispatchers (one for every EventListener that has activated it).
+ ///
+ /// Logically a particular EventDispatcher belongs to exactly one EventSource and exactly
+ /// one EventListener (alhtough EventDispatcher does not 'remember' the EventSource it is
+ /// associated with.
+ /// </summary>
+ internal class EventDispatcher
+ {
+ internal EventDispatcher(EventDispatcher next, bool[] eventEnabled, EventListener listener)
+ {
+ m_Next = next;
+ m_EventEnabled = eventEnabled;
+ m_Listener = listener;
+ }
+
+ // Instance fields
+ readonly internal EventListener m_Listener; // The dispatcher this entry is for
+ internal bool[] m_EventEnabled; // For every event in a the eventSource, is it enabled?
+#if FEATURE_ACTIVITYSAMPLING
+ internal bool m_activityFilteringEnabled; // does THIS EventSource have activity filtering turned on for this listener?
+#endif // FEATURE_ACTIVITYSAMPLING
+
+ // Only guaranteed to exist after a InsureInit()
+ internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers
+ // Of all listeners for that eventSource.
+ }
+
+ /// <summary>
+ /// Flags that can be used with EventSource.GenerateManifest to control how the ETW manifest for the EventSource is
+ /// generated.
+ /// </summary>
+ [Flags]
+ public enum EventManifestOptions
+ {
+ /// <summary>
+ /// Only the resources associated with current UI culture are included in the manifest
+ /// </summary>
+ None = 0x0,
+ /// <summary>
+ /// Throw exceptions for any inconsistency encountered
+ /// </summary>
+ Strict = 0x1,
+ /// <summary>
+ /// Generate a "resources" node under "localization" for every satellite assembly provided
+ /// </summary>
+ AllCultures = 0x2,
+ /// <summary>
+ /// Generate the manifest only if the event source needs to be registered on the machine,
+ /// otherwise return null (but still perform validation if Strict is specified)
+ /// </summary>
+ OnlyIfNeededForRegistration = 0x4,
+ /// <summary>
+ /// When generating the manifest do *not* enforce the rule that the current EventSource class
+ /// must be the base class for the user-defined type passed in. This allows validation of .net
+ /// event sources using the new validation code
+ /// </summary>
+ AllowEventSourceOverride = 0x8,
+ }
+
+ /// <summary>
+ /// ManifestBuilder is designed to isolate the details of the message of the event from the
+ /// rest of EventSource. This one happens to create XML.
+ /// </summary>
+ internal partial class ManifestBuilder
+ {
+ /// <summary>
+ /// Build a manifest for 'providerName' with the given GUID, which will be packaged into 'dllName'.
+ /// 'resources, is a resource manager. If specified all messages are localized using that manager.
+ /// </summary>
+ public ManifestBuilder(string providerName, Guid providerGuid, string dllName, ResourceManager resources,
+ EventManifestOptions flags)
+ {
+#if FEATURE_MANAGED_ETW_CHANNELS
+ this.providerName = providerName;
+#endif
+ this.flags = flags;
+
+ this.resources = resources;
+ sb = new StringBuilder();
+ events = new StringBuilder();
+ templates = new StringBuilder();
+ opcodeTab = new Dictionary<int, string>();
+ stringTab = new Dictionary<string, string>();
+ errors = new List<string>();
+ perEventByteArrayArgIndices = new Dictionary<string, List<int>>();
+
+ sb.AppendLine("<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
+ sb.AppendLine(" <instrumentation xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:win=\"http://manifests.microsoft.com/win/2004/08/windows/events\">");
+ sb.AppendLine(" <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">");
+ sb.Append("<provider name=\"").Append(providerName).
+ Append("\" guid=\"{").Append(providerGuid.ToString()).Append("}");
+ if (dllName != null)
+ sb.Append("\" resourceFileName=\"").Append(dllName).Append("\" messageFileName=\"").Append(dllName);
+
+ var symbolsName = providerName.Replace("-", "").Replace(".", "_"); // Period and - are illegal replace them.
+ sb.Append("\" symbol=\"").Append(symbolsName);
+ sb.Append("\">").AppendLine();
+ }
+
+ public void AddOpcode(string name, int value)
+ {
+ if ((flags & EventManifestOptions.Strict) != 0)
+ {
+ if (value <= 10 || value >= 239)
+ {
+ ManifestError(Resources.GetResourceString("EventSource_IllegalOpcodeValue", name, value));
+ }
+ string prevName;
+ if (opcodeTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_OpcodeCollision", name, prevName, value));
+ }
+ }
+ opcodeTab[value] = name;
+ }
+ public void AddTask(string name, int value)
+ {
+ if ((flags & EventManifestOptions.Strict) != 0)
+ {
+ if (value <= 0 || value >= 65535)
+ {
+ ManifestError(Resources.GetResourceString("EventSource_IllegalTaskValue", name, value));
+ }
+ string prevName;
+ if (taskTab != null && taskTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_TaskCollision", name, prevName, value));
+ }
+ }
+ if (taskTab == null)
+ taskTab = new Dictionary<int, string>();
+ taskTab[value] = name;
+ }
+ public void AddKeyword(string name, ulong value)
+ {
+ if ((value & (value - 1)) != 0) // Is it a power of 2?
+ {
+ ManifestError(Resources.GetResourceString("EventSource_KeywordNeedPowerOfTwo", "0x" + value.ToString("x", CultureInfo.CurrentCulture), name), true);
+ }
+ if ((flags & EventManifestOptions.Strict) != 0)
+ {
+ if (value >= 0x0000100000000000UL && !name.StartsWith("Session", StringComparison.Ordinal))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_IllegalKeywordsValue", name, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
+ }
+ string prevName;
+ if (keywordTab != null && keywordTab.TryGetValue(value, out prevName) && !name.Equals(prevName, StringComparison.Ordinal))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_KeywordCollision", name, prevName, "0x" + value.ToString("x", CultureInfo.CurrentCulture)));
+ }
+ }
+ if (keywordTab == null)
+ keywordTab = new Dictionary<ulong, string>();
+ keywordTab[value] = name;
+ }
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ /// <summary>
+ /// Add a channel. channelAttribute can be null
+ /// </summary>
+ public void AddChannel(string name, int value, EventChannelAttribute channelAttribute)
+ {
+ EventChannel chValue = (EventChannel)value;
+ if (value < (int)EventChannel.Admin || value > 255)
+ ManifestError(Resources.GetResourceString("EventSource_EventChannelOutOfRange", name, value));
+ else if (chValue >= EventChannel.Admin && chValue <= EventChannel.Debug &&
+ channelAttribute != null && EventChannelToChannelType(chValue) != channelAttribute.EventChannelType)
+ {
+ // we want to ensure developers do not define EventChannels that conflict with the builtin ones,
+ // but we want to allow them to override the default ones...
+ ManifestError(Resources.GetResourceString("EventSource_ChannelTypeDoesNotMatchEventChannelValue",
+ name, ((EventChannel)value).ToString()));
+ }
+
+ // TODO: validate there are no conflicting manifest exposed names (generally following the format "provider/type")
+
+ ulong kwd = GetChannelKeyword(chValue);
+
+ if (channelTab == null)
+ channelTab = new Dictionary<int, ChannelInfo>(4);
+ channelTab[value] = new ChannelInfo { Name = name, Keywords = kwd, Attribs = channelAttribute };
+ }
+
+ private EventChannelType EventChannelToChannelType(EventChannel channel)
+ {
+#if !ES_BUILD_STANDALONE
+ Debug.Assert(channel >= EventChannel.Admin && channel <= EventChannel.Debug);
+#endif
+ return (EventChannelType)((int)channel - (int)EventChannel.Admin + (int)EventChannelType.Admin);
+ }
+ private EventChannelAttribute GetDefaultChannelAttribute(EventChannel channel)
+ {
+ EventChannelAttribute attrib = new EventChannelAttribute();
+ attrib.EventChannelType = EventChannelToChannelType(channel);
+ if (attrib.EventChannelType <= EventChannelType.Operational)
+ attrib.Enabled = true;
+ return attrib;
+ }
+
+ public ulong[] GetChannelData()
+ {
+ if (this.channelTab == null)
+ {
+ return new ulong[0];
+ }
+
+ // We create an array indexed by the channel id for fast look up.
+ // E.g. channelMask[Admin] will give you the bit mask for Admin channel.
+ int maxkey = -1;
+ foreach (var item in this.channelTab.Keys)
+ {
+ if (item > maxkey)
+ {
+ maxkey = item;
+ }
+ }
+
+ ulong[] channelMask = new ulong[maxkey + 1];
+ foreach (var item in this.channelTab)
+ {
+ channelMask[item.Key] = item.Value.Keywords;
+ }
+
+ return channelMask;
+ }
+
+#endif
+ public void StartEvent(string eventName, EventAttribute eventAttribute)
+ {
+ Debug.Assert(numParams == 0);
+ Debug.Assert(this.eventName == null);
+ this.eventName = eventName;
+ numParams = 0;
+ byteArrArgIndices = null;
+
+ events.Append(" <event").
+ Append(" value=\"").Append(eventAttribute.EventId).Append("\"").
+ Append(" version=\"").Append(eventAttribute.Version).Append("\"").
+ Append(" level=\"").Append(GetLevelName(eventAttribute.Level)).Append("\"").
+ Append(" symbol=\"").Append(eventName).Append("\"");
+
+ // at this point we add to the manifest's stringTab a message that is as-of-yet
+ // "untranslated to manifest convention", b/c we don't have the number or position
+ // of any byte[] args (which require string format index updates)
+ WriteMessageAttrib(events, "event", eventName, eventAttribute.Message);
+
+ if (eventAttribute.Keywords != 0)
+ events.Append(" keywords=\"").Append(GetKeywords((ulong)eventAttribute.Keywords, eventName)).Append("\"");
+ if (eventAttribute.Opcode != 0)
+ events.Append(" opcode=\"").Append(GetOpcodeName(eventAttribute.Opcode, eventName)).Append("\"");
+ if (eventAttribute.Task != 0)
+ events.Append(" task=\"").Append(GetTaskName(eventAttribute.Task, eventName)).Append("\"");
+#if FEATURE_MANAGED_ETW_CHANNELS
+ if (eventAttribute.Channel != 0)
+ {
+ events.Append(" channel=\"").Append(GetChannelName(eventAttribute.Channel, eventName, eventAttribute.Message)).Append("\"");
+ }
+#endif
+ }
+
+ public void AddEventParameter(Type type, string name)
+ {
+ if (numParams == 0)
+ templates.Append(" <template tid=\"").Append(eventName).Append("Args\">").AppendLine();
+ if (type == typeof(byte[]))
+ {
+ // mark this index as "extraneous" (it has no parallel in the managed signature)
+ // we use these values in TranslateToManifestConvention()
+ if (byteArrArgIndices == null)
+ byteArrArgIndices = new List<int>(4);
+ byteArrArgIndices.Add(numParams);
+
+ // add an extra field to the template representing the length of the binary blob
+ numParams++;
+ templates.Append(" <data name=\"").Append(name).Append("Size\" inType=\"win:UInt32\"/>").AppendLine();
+ }
+ numParams++;
+ templates.Append(" <data name=\"").Append(name).Append("\" inType=\"").Append(GetTypeName(type)).Append("\"");
+ // TODO: for 'byte*' types it assumes the user provided length is named using the same naming convention
+ // as for 'byte[]' args (blob_arg_name + "Size")
+ if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte))
+ {
+ // add "length" attribute to the "blob" field in the template (referencing the field added above)
+ templates.Append(" length=\"").Append(name).Append("Size\"");
+ }
+ // ETW does not support 64-bit value maps, so we don't specify these as ETW maps
+ if (type.IsEnum() && Enum.GetUnderlyingType(type) != typeof(UInt64) && Enum.GetUnderlyingType(type) != typeof(Int64))
+ {
+ templates.Append(" map=\"").Append(type.Name).Append("\"");
+ if (mapsTab == null)
+ mapsTab = new Dictionary<string, Type>();
+ if (!mapsTab.ContainsKey(type.Name))
+ mapsTab.Add(type.Name, type); // Remember that we need to dump the type enumeration
+ }
+
+ templates.Append("/>").AppendLine();
+ }
+ public void EndEvent()
+ {
+ if (numParams > 0)
+ {
+ templates.Append(" </template>").AppendLine();
+ events.Append(" template=\"").Append(eventName).Append("Args\"");
+ }
+ events.Append("/>").AppendLine();
+
+ if (byteArrArgIndices != null)
+ perEventByteArrayArgIndices[eventName] = byteArrArgIndices;
+
+ // at this point we have all the information we need to translate the C# Message
+ // to the manifest string we'll put in the stringTab
+ string msg;
+ if (stringTab.TryGetValue("event_" + eventName, out msg))
+ {
+ msg = TranslateToManifestConvention(msg, eventName);
+ stringTab["event_" + eventName] = msg;
+ }
+
+ eventName = null;
+ numParams = 0;
+ byteArrArgIndices = null;
+ }
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // Channel keywords are generated one per channel to allow channel based filtering in event viewer. These keywords are autogenerated
+ // by mc.exe for compiling a manifest and are based on the order of the channels (fields) in the Channels inner class (when advanced
+ // channel support is enabled), or based on the order the predefined channels appear in the EventAttribute properties (for simple
+ // support). The manifest generated *MUST* have the channels specified in the same order (that's how our computed keywords are mapped
+ // to channels by the OS infrastructure).
+ // If channelKeyworkds is present, and has keywords bits in the ValidPredefinedChannelKeywords then it is
+ // assumed that that the keyword for that channel should be that bit.
+ // otherwise we allocate a channel bit for the channel.
+ // explicit channel bits are only used by WCF to mimic an existing manifest,
+ // so we don't dont do error checking.
+ public ulong GetChannelKeyword(EventChannel channel, ulong channelKeyword = 0)
+ {
+ // strip off any non-channel keywords, since we are only interested in channels here.
+ channelKeyword &= ValidPredefinedChannelKeywords;
+ if (channelTab == null)
+ {
+ channelTab = new Dictionary<int, ChannelInfo>(4);
+ }
+
+ if (channelTab.Count == MaxCountChannels)
+ ManifestError(Resources.GetResourceString("EventSource_MaxChannelExceeded"));
+
+ ChannelInfo info;
+ if (!channelTab.TryGetValue((int)channel, out info))
+ {
+ // If we were not given an explicit channel, allocate one.
+ if (channelKeyword != 0)
+ {
+ channelKeyword = nextChannelKeywordBit;
+ nextChannelKeywordBit >>= 1;
+ }
+ }
+ else
+ {
+ channelKeyword = info.Keywords;
+ }
+
+ return channelKeyword;
+ }
+#endif
+
+ public byte[] CreateManifest()
+ {
+ string str = CreateManifestString();
+ return Encoding.UTF8.GetBytes(str);
+ }
+
+ public IList<string> Errors { get { return errors; } }
+
+ /// <summary>
+ /// When validating an event source it adds the error to the error collection.
+ /// When not validating it throws an exception if runtimeCritical is "true".
+ /// Otherwise the error is ignored.
+ /// </summary>
+ /// <param name="msg"></param>
+ /// <param name="runtimeCritical"></param>
+ public void ManifestError(string msg, bool runtimeCritical = false)
+ {
+ if ((flags & EventManifestOptions.Strict) != 0)
+ errors.Add(msg);
+ else if (runtimeCritical)
+ throw new ArgumentException(msg);
+ }
+
+ private string CreateManifestString()
+ {
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // Write out the channels
+ if (channelTab != null)
+ {
+ sb.Append(" <channels>").AppendLine();
+ var sortedChannels = new List<KeyValuePair<int, ChannelInfo>>();
+ foreach (KeyValuePair<int, ChannelInfo> p in channelTab) { sortedChannels.Add(p); }
+ sortedChannels.Sort((p1, p2) => -Comparer<ulong>.Default.Compare(p1.Value.Keywords, p2.Value.Keywords));
+ foreach (var kvpair in sortedChannels)
+ {
+ int channel = kvpair.Key;
+ ChannelInfo channelInfo = kvpair.Value;
+
+ string channelType = null;
+ string elementName = "channel";
+ bool enabled = false;
+ string fullName = null;
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ string isolation = null;
+ string access = null;
+#endif
+ if (channelInfo.Attribs != null)
+ {
+ var attribs = channelInfo.Attribs;
+ if (Enum.IsDefined(typeof(EventChannelType), attribs.EventChannelType))
+ channelType = attribs.EventChannelType.ToString();
+ enabled = attribs.Enabled;
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ if (attribs.ImportChannel != null)
+ {
+ fullName = attribs.ImportChannel;
+ elementName = "importChannel";
+ }
+ if (Enum.IsDefined(typeof(EventChannelIsolation), attribs.Isolation))
+ isolation = attribs.Isolation.ToString();
+ access = attribs.Access;
+#endif
+ }
+ if (fullName == null)
+ fullName = providerName + "/" + channelInfo.Name;
+
+ sb.Append(" <").Append(elementName);
+ sb.Append(" chid=\"").Append(channelInfo.Name).Append("\"");
+ sb.Append(" name=\"").Append(fullName).Append("\"");
+ if (elementName == "channel") // not applicable to importChannels.
+ {
+ WriteMessageAttrib(sb, "channel", channelInfo.Name, null);
+ sb.Append(" value=\"").Append(channel).Append("\"");
+ if (channelType != null)
+ sb.Append(" type=\"").Append(channelType).Append("\"");
+ sb.Append(" enabled=\"").Append(enabled.ToString().ToLower()).Append("\"");
+#if FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+ if (access != null)
+ sb.Append(" access=\"").Append(access).Append("\"");
+ if (isolation != null)
+ sb.Append(" isolation=\"").Append(isolation).Append("\"");
+#endif
+ }
+ sb.Append("/>").AppendLine();
+ }
+ sb.Append(" </channels>").AppendLine();
+ }
+#endif
+
+ // Write out the tasks
+ if (taskTab != null)
+ {
+
+ sb.Append(" <tasks>").AppendLine();
+ var sortedTasks = new List<int>(taskTab.Keys);
+ sortedTasks.Sort();
+ foreach (int task in sortedTasks)
+ {
+ sb.Append(" <task");
+ WriteNameAndMessageAttribs(sb, "task", taskTab[task]);
+ sb.Append(" value=\"").Append(task).Append("\"/>").AppendLine();
+ }
+ sb.Append(" </tasks>").AppendLine();
+ }
+
+ // Write out the maps
+ if (mapsTab != null)
+ {
+ sb.Append(" <maps>").AppendLine();
+ foreach (Type enumType in mapsTab.Values)
+ {
+ bool isbitmap = EventSource.GetCustomAttributeHelper(enumType, typeof(FlagsAttribute), flags) != null;
+ string mapKind = isbitmap ? "bitMap" : "valueMap";
+ sb.Append(" <").Append(mapKind).Append(" name=\"").Append(enumType.Name).Append("\">").AppendLine();
+
+ // write out each enum value
+ FieldInfo[] staticFields = enumType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static);
+ foreach (FieldInfo staticField in staticFields)
+ {
+ object constantValObj = staticField.GetRawConstantValue();
+ if (constantValObj != null)
+ {
+ long hexValue;
+ if (constantValObj is int)
+ hexValue = ((int)constantValObj);
+ else if (constantValObj is long)
+ hexValue = ((long)constantValObj);
+ else
+ continue;
+
+ // ETW requires all bitmap values to be powers of 2. Skip the ones that are not.
+ // TODO: Warn people about the dropping of values.
+ if (isbitmap && ((hexValue & (hexValue - 1)) != 0 || hexValue == 0))
+ continue;
+
+ sb.Append(" <map value=\"0x").Append(hexValue.ToString("x", CultureInfo.InvariantCulture)).Append("\"");
+ WriteMessageAttrib(sb, "map", enumType.Name + "." + staticField.Name, staticField.Name);
+ sb.Append("/>").AppendLine();
+ }
+ }
+ sb.Append(" </").Append(mapKind).Append(">").AppendLine();
+ }
+ sb.Append(" </maps>").AppendLine();
+ }
+
+ // Write out the opcodes
+ sb.Append(" <opcodes>").AppendLine();
+ var sortedOpcodes = new List<int>(opcodeTab.Keys);
+ sortedOpcodes.Sort();
+ foreach (int opcode in sortedOpcodes)
+ {
+ sb.Append(" <opcode");
+ WriteNameAndMessageAttribs(sb, "opcode", opcodeTab[opcode]);
+ sb.Append(" value=\"").Append(opcode).Append("\"/>").AppendLine();
+ }
+ sb.Append(" </opcodes>").AppendLine();
+
+ // Write out the keywords
+ if (keywordTab != null)
+ {
+ sb.Append(" <keywords>").AppendLine();
+ var sortedKeywords = new List<ulong>(keywordTab.Keys);
+ sortedKeywords.Sort();
+ foreach (ulong keyword in sortedKeywords)
+ {
+ sb.Append(" <keyword");
+ WriteNameAndMessageAttribs(sb, "keyword", keywordTab[keyword]);
+ sb.Append(" mask=\"0x").Append(keyword.ToString("x", CultureInfo.InvariantCulture)).Append("\"/>").AppendLine();
+ }
+ sb.Append(" </keywords>").AppendLine();
+ }
+
+ sb.Append(" <events>").AppendLine();
+ sb.Append(events);
+ sb.Append(" </events>").AppendLine();
+
+ sb.Append(" <templates>").AppendLine();
+ if (templates.Length > 0)
+ {
+ sb.Append(templates);
+ }
+ else
+ {
+ // Work around a cornercase ETW issue where a manifest with no templates causes
+ // ETW events to not get sent to their associated channel.
+ sb.Append(" <template tid=\"_empty\"></template>").AppendLine();
+ }
+ sb.Append(" </templates>").AppendLine();
+
+ sb.Append("</provider>").AppendLine();
+ sb.Append("</events>").AppendLine();
+ sb.Append("</instrumentation>").AppendLine();
+
+ // Output the localization information.
+ sb.Append("<localization>").AppendLine();
+
+ List<CultureInfo> cultures = null;
+ if (resources != null && (flags & EventManifestOptions.AllCultures) != 0)
+ {
+ cultures = GetSupportedCultures(resources);
+ }
+ else
+ {
+ cultures = new List<CultureInfo>();
+ cultures.Add(CultureInfo.CurrentUICulture);
+ }
+#if ES_BUILD_STANDALONE || ES_BUILD_PN
+ var sortedStrings = new List<string>(stringTab.Keys);
+ sortedStrings.Sort();
+#else
+ // DD 947936
+ var sortedStrings = new string[stringTab.Keys.Count];
+ stringTab.Keys.CopyTo(sortedStrings, 0);
+ // Avoid using public Array.Sort as that attempts to access BinaryCompatibility. Unfortunately FrameworkEventSource gets called
+ // very early in the app domain creation, when _FusionStore is not set up yet, resulting in a failure to run the static constructory
+ // for BinaryCompatibility. This failure is then cached and a TypeInitializationException is thrown every time some code attampts to
+ // access BinaryCompatibility.
+ ArraySortHelper<string>.IntrospectiveSort(sortedStrings, 0, sortedStrings.Length, string.Compare);
+#endif
+ foreach (var ci in cultures)
+ {
+ sb.Append(" <resources culture=\"").Append(ci.Name).Append("\">").AppendLine();
+ sb.Append(" <stringTable>").AppendLine();
+
+ foreach (var stringKey in sortedStrings)
+ {
+ string val = GetLocalizedMessage(stringKey, ci, etwFormat: true);
+ sb.Append(" <string id=\"").Append(stringKey).Append("\" value=\"").Append(val).Append("\"/>").AppendLine();
+ }
+ sb.Append(" </stringTable>").AppendLine();
+ sb.Append(" </resources>").AppendLine();
+ }
+ sb.Append("</localization>").AppendLine();
+ sb.AppendLine("</instrumentationManifest>");
+ return sb.ToString();
+ }
+
+ #region private
+ private void WriteNameAndMessageAttribs(StringBuilder stringBuilder, string elementName, string name)
+ {
+ stringBuilder.Append(" name=\"").Append(name).Append("\"");
+ WriteMessageAttrib(sb, elementName, name, name);
+ }
+ private void WriteMessageAttrib(StringBuilder stringBuilder, string elementName, string name, string value)
+ {
+ string key = elementName + "_" + name;
+ // See if the user wants things localized.
+ if (resources != null)
+ {
+ // resource fallback: strings in the neutral culture will take precedence over inline strings
+ string localizedString = resources.GetString(key, CultureInfo.InvariantCulture);
+ if (localizedString != null)
+ value = localizedString;
+ }
+ if (value == null)
+ return;
+
+ stringBuilder.Append(" message=\"$(string.").Append(key).Append(")\"");
+ string prevValue;
+ if (stringTab.TryGetValue(key, out prevValue) && !prevValue.Equals(value))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_DuplicateStringKey", key), true);
+ return;
+ }
+
+ stringTab[key] = value;
+ }
+ internal string GetLocalizedMessage(string key, CultureInfo ci, bool etwFormat)
+ {
+ string value = null;
+ if (resources != null)
+ {
+ string localizedString = resources.GetString(key, ci);
+ if (localizedString != null)
+ {
+ value = localizedString;
+ if (etwFormat && key.StartsWith("event_", StringComparison.Ordinal))
+ {
+ var evtName = key.Substring("event_".Length);
+ value = TranslateToManifestConvention(value, evtName);
+ }
+ }
+ }
+ if (etwFormat && value == null)
+ stringTab.TryGetValue(key, out value);
+
+ return value;
+ }
+
+ /// <summary>
+ /// There's no API to enumerate all languages an assembly is localized into, so instead
+ /// we enumerate through all the "known" cultures and attempt to load a corresponding satellite
+ /// assembly
+ /// </summary>
+ /// <param name="resources"></param>
+ /// <returns></returns>
+ private static List<CultureInfo> GetSupportedCultures(ResourceManager resources)
+ {
+ var cultures = new List<CultureInfo>();
+
+ if (!cultures.Contains(CultureInfo.CurrentUICulture))
+ cultures.Insert(0, CultureInfo.CurrentUICulture);
+ return cultures;
+ }
+
+ private static string GetLevelName(EventLevel level)
+ {
+ return (((int)level >= 16) ? "" : "win:") + level.ToString();
+ }
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ private string GetChannelName(EventChannel channel, string eventName, string eventMessage)
+ {
+ ChannelInfo info = null;
+ if (channelTab == null || !channelTab.TryGetValue((int)channel, out info))
+ {
+ if (channel < EventChannel.Admin) // || channel > EventChannel.Debug)
+ ManifestError(Resources.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
+
+ // allow channels to be auto-defined. The well known ones get their well known names, and the
+ // rest get names Channel<N>. This allows users to modify the Manifest if they want more advanced features.
+ if (channelTab == null)
+ channelTab = new Dictionary<int, ChannelInfo>(4);
+
+ string channelName = channel.ToString(); // For well know channels this is a nice name, otherwise a number
+ if (EventChannel.Debug < channel)
+ channelName = "Channel" + channelName; // Add a 'Channel' prefix for numbers.
+
+ AddChannel(channelName, (int)channel, GetDefaultChannelAttribute(channel));
+ if (!channelTab.TryGetValue((int)channel, out info))
+ ManifestError(Resources.GetResourceString("EventSource_UndefinedChannel", channel, eventName));
+ }
+ // events that specify admin channels *must* have non-null "Message" attributes
+ if (resources != null && eventMessage == null)
+ eventMessage = resources.GetString("event_" + eventName, CultureInfo.InvariantCulture);
+ if (info.Attribs.EventChannelType == EventChannelType.Admin && eventMessage == null)
+ ManifestError(Resources.GetResourceString("EventSource_EventWithAdminChannelMustHaveMessage", eventName, info.Name));
+ return info.Name;
+ }
+#endif
+ private string GetTaskName(EventTask task, string eventName)
+ {
+ if (task == EventTask.None)
+ return "";
+
+ string ret;
+ if (taskTab == null)
+ taskTab = new Dictionary<int, string>();
+ if (!taskTab.TryGetValue((int)task, out ret))
+ ret = taskTab[(int)task] = eventName;
+ return ret;
+ }
+
+ private string GetOpcodeName(EventOpcode opcode, string eventName)
+ {
+ switch (opcode)
+ {
+ case EventOpcode.Info:
+ return "win:Info";
+ case EventOpcode.Start:
+ return "win:Start";
+ case EventOpcode.Stop:
+ return "win:Stop";
+ case EventOpcode.DataCollectionStart:
+ return "win:DC_Start";
+ case EventOpcode.DataCollectionStop:
+ return "win:DC_Stop";
+ case EventOpcode.Extension:
+ return "win:Extension";
+ case EventOpcode.Reply:
+ return "win:Reply";
+ case EventOpcode.Resume:
+ return "win:Resume";
+ case EventOpcode.Suspend:
+ return "win:Suspend";
+ case EventOpcode.Send:
+ return "win:Send";
+ case EventOpcode.Receive:
+ return "win:Receive";
+ }
+
+ string ret;
+ if (opcodeTab == null || !opcodeTab.TryGetValue((int)opcode, out ret))
+ {
+ ManifestError(Resources.GetResourceString("EventSource_UndefinedOpcode", opcode, eventName), true);
+ ret = null;
+ }
+ return ret;
+ }
+
+ private string GetKeywords(ulong keywords, string eventName)
+ {
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // ignore keywords associate with channels
+ // See ValidPredefinedChannelKeywords def for more.
+ keywords &= ~ValidPredefinedChannelKeywords;
+#endif
+
+ string ret = "";
+ for (ulong bit = 1; bit != 0; bit <<= 1)
+ {
+ if ((keywords & bit) != 0)
+ {
+ string keyword = null;
+ if ((keywordTab == null || !keywordTab.TryGetValue(bit, out keyword)) &&
+ (bit >= (ulong)0x1000000000000))
+ {
+ // do not report Windows reserved keywords in the manifest (this allows the code
+ // to be resilient to potential renaming of these keywords)
+ keyword = string.Empty;
+ }
+ if (keyword == null)
+ {
+ ManifestError(Resources.GetResourceString("EventSource_UndefinedKeyword", "0x" + bit.ToString("x", CultureInfo.CurrentCulture), eventName), true);
+ keyword = string.Empty;
+ }
+ if (ret.Length != 0 && keyword.Length != 0)
+ ret = ret + " ";
+ ret = ret + keyword;
+ }
+ }
+ return ret;
+ }
+
+ private string GetTypeName(Type type)
+ {
+ if (type.IsEnum())
+ {
+ FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
+ var typeName = GetTypeName(fields[0].FieldType);
+ return typeName.Replace("win:Int", "win:UInt"); // ETW requires enums to be unsigned.
+ }
+
+ return GetTypeNameHelper(type);
+ }
+
+ private static void UpdateStringBuilder(ref StringBuilder stringBuilder, string eventMessage, int startIndex, int count)
+ {
+ if (stringBuilder == null)
+ stringBuilder = new StringBuilder();
+ stringBuilder.Append(eventMessage, startIndex, count);
+ }
+
+ private static readonly string[] s_escapes = { "&amp;", "&lt;", "&gt;", "&apos;", "&quot;", "%r", "%n", "%t" };
+ // Manifest messages use %N conventions for their message substitutions. Translate from
+ // .NET conventions. We can't use RegEx for this (we are in mscorlib), so we do it 'by hand'
+ private string TranslateToManifestConvention(string eventMessage, string evtName)
+ {
+ StringBuilder stringBuilder = null; // We lazily create this
+ int writtenSoFar = 0;
+ int chIdx = -1;
+ for (int i = 0; ;)
+ {
+ if (i >= eventMessage.Length)
+ {
+ if (stringBuilder == null)
+ return eventMessage;
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
+ return stringBuilder.ToString();
+ }
+
+ if (eventMessage[i] == '%')
+ {
+ // handle format message escaping character '%' by escaping it
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
+ stringBuilder.Append("%%");
+ i++;
+ writtenSoFar = i;
+ }
+ else if (i < eventMessage.Length - 1 &&
+ (eventMessage[i] == '{' && eventMessage[i + 1] == '{' || eventMessage[i] == '}' && eventMessage[i + 1] == '}'))
+ {
+ // handle C# escaped '{" and '}'
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
+ stringBuilder.Append(eventMessage[i]);
+ i++; i++;
+ writtenSoFar = i;
+ }
+ else if (eventMessage[i] == '{')
+ {
+ int leftBracket = i;
+ i++;
+ int argNum = 0;
+ while (i < eventMessage.Length && Char.IsDigit(eventMessage[i]))
+ {
+ argNum = argNum * 10 + eventMessage[i] - '0';
+ i++;
+ }
+ if (i < eventMessage.Length && eventMessage[i] == '}')
+ {
+ i++;
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, leftBracket - writtenSoFar);
+ int manIndex = TranslateIndexToManifestConvention(argNum, evtName);
+ stringBuilder.Append('%').Append(manIndex);
+ // An '!' after the insert specifier {n} will be interpreted as a literal.
+ // We'll escape it so that mc.exe does not attempt to consider it the
+ // beginning of a format string.
+ if (i < eventMessage.Length && eventMessage[i] == '!')
+ {
+ i++;
+ stringBuilder.Append("%!");
+ }
+ writtenSoFar = i;
+ }
+ else
+ {
+ ManifestError(Resources.GetResourceString("EventSource_UnsupportedMessageProperty", evtName, eventMessage));
+ }
+ }
+ else if ((chIdx = "&<>'\"\r\n\t".IndexOf(eventMessage[i])) >= 0)
+ {
+ UpdateStringBuilder(ref stringBuilder, eventMessage, writtenSoFar, i - writtenSoFar);
+ i++;
+ stringBuilder.Append(s_escapes[chIdx]);
+ writtenSoFar = i;
+ }
+ else
+ i++;
+ }
+ }
+
+ private int TranslateIndexToManifestConvention(int idx, string evtName)
+ {
+ List<int> byteArrArgIndices;
+ if (perEventByteArrayArgIndices.TryGetValue(evtName, out byteArrArgIndices))
+ {
+ foreach (var byArrIdx in byteArrArgIndices)
+ {
+ if (idx >= byArrIdx)
+ ++idx;
+ else
+ break;
+ }
+ }
+ return idx + 1;
+ }
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ class ChannelInfo
+ {
+ public string Name;
+ public ulong Keywords;
+ public EventChannelAttribute Attribs;
+ }
+#endif
+
+ Dictionary<int, string> opcodeTab;
+ Dictionary<int, string> taskTab;
+#if FEATURE_MANAGED_ETW_CHANNELS
+ Dictionary<int, ChannelInfo> channelTab;
+#endif
+ Dictionary<ulong, string> keywordTab;
+ Dictionary<string, Type> mapsTab;
+
+ Dictionary<string, string> stringTab; // Maps unlocalized strings to localized ones
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ // WCF used EventSource to mimic a existing ETW manifest. To support this
+ // in just their case, we allowed them to specify the keywords associated
+ // with their channels explicitly. ValidPredefinedChannelKeywords is
+ // this set of channel keywords that we allow to be explicitly set. You
+ // can ignore these bits otherwise.
+ internal const ulong ValidPredefinedChannelKeywords = 0xF000000000000000;
+ ulong nextChannelKeywordBit = 0x8000000000000000; // available Keyword bit to be used for next channel definition, grows down
+ const int MaxCountChannels = 8; // a manifest can defined at most 8 ETW channels
+#endif
+
+ StringBuilder sb; // Holds the provider information.
+ StringBuilder events; // Holds the events.
+ StringBuilder templates;
+
+#if FEATURE_MANAGED_ETW_CHANNELS
+ string providerName;
+#endif
+ ResourceManager resources; // Look up localized strings here.
+ EventManifestOptions flags;
+ IList<string> errors; // list of currently encountered errors
+ Dictionary<string, List<int>> perEventByteArrayArgIndices; // "event_name" -> List_of_Indices_of_Byte[]_Arg
+
+ // State we track between StartEvent and EndEvent.
+ string eventName; // Name of the event currently being processed.
+ int numParams; // keeps track of the number of args the event has.
+ List<int> byteArrArgIndices; // keeps track of the index of each byte[] argument
+ #endregion
+ }
+
+ /// <summary>
+ /// Used to send the m_rawManifest into the event dispatcher as a series of events.
+ /// </summary>
+ internal struct ManifestEnvelope
+ {
+ public const int MaxChunkSize = 0xFF00;
+ public enum ManifestFormats : byte
+ {
+ SimpleXmlFormat = 1, // simply dump the XML manifest as UTF8
+ }
+
+#if FEATURE_MANAGED_ETW
+ public ManifestFormats Format;
+ public byte MajorVersion;
+ public byte MinorVersion;
+ public byte Magic;
+ public ushort TotalChunks;
+ public ushort ChunkNumber;
+#endif
+ };
+
+ #endregion
+}
+
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/EventSourceException.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSourceException.cs
new file mode 100644
index 0000000000..88b8da93cd
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/EventSourceException.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+#if ES_BUILD_STANDALONE
+using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Exception that is thrown when an error occurs during EventSource operation.
+ /// </summary>
+#if !ES_BUILD_PCL
+ [Serializable]
+#endif
+ public class EventSourceException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the EventSourceException class.
+ /// </summary>
+ public EventSourceException() :
+ base(Resources.GetResourceString("EventSource_ListenerWriteFailure")) { }
+
+ /// <summary>
+ /// Initializes a new instance of the EventSourceException class with a specified error message.
+ /// </summary>
+ public EventSourceException(string message) : base(message) { }
+
+ /// <summary>
+ /// Initializes a new instance of the EventSourceException class with a specified error message
+ /// and a reference to the inner exception that is the cause of this exception.
+ /// </summary>
+ public EventSourceException(string message, Exception innerException) : base(message, innerException) { }
+
+#if !ES_BUILD_PCL
+ /// <summary>
+ /// Initializes a new instance of the EventSourceException class with serialized data.
+ /// </summary>
+ protected EventSourceException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+#endif
+
+ internal EventSourceException(Exception innerException) :
+ base(Resources.GetResourceString("EventSource_ListenerWriteFailure"), innerException) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/StubEnvironment.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/StubEnvironment.cs
new file mode 100644
index 0000000000..b365841d5f
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/StubEnvironment.cs
@@ -0,0 +1,381 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing.Internal
+#else
+namespace System.Diagnostics.Tracing.Internal
+#endif
+{
+#if ES_BUILD_AGAINST_DOTNET_V35
+ using Microsoft.Internal;
+#endif
+ using Microsoft.Reflection;
+ using System.Reflection;
+
+ internal static class Environment
+ {
+ public static readonly string NewLine = System.Environment.NewLine;
+
+ public static int TickCount
+ { get { return System.Environment.TickCount; } }
+
+ public static string GetResourceString(string key, params object[] args)
+ {
+ string fmt = rm.GetString(key);
+ if (fmt != null)
+ return string.Format(fmt, args);
+
+ string sargs = String.Empty;
+ foreach(var arg in args)
+ {
+ if (sargs != String.Empty)
+ sargs += ", ";
+ sargs += arg.ToString();
+ }
+
+ return key + " (" + sargs + ")";
+ }
+
+ public static string GetRuntimeResourceString(string key, params object[] args)
+ {
+ return GetResourceString(key, args);
+ }
+
+ private static System.Resources.ResourceManager rm = new System.Resources.ResourceManager("Microsoft.Diagnostics.Tracing.Messages", typeof(Environment).Assembly());
+ }
+}
+
+#if ES_BUILD_AGAINST_DOTNET_V35
+
+namespace Microsoft.Diagnostics.Contracts.Internal
+{
+ internal class Contract
+ {
+ public static void Assert(bool invariant)
+ {
+ Assert(invariant, string.Empty);
+ }
+ public static void Assert(bool invariant, string message)
+ {
+ if (!invariant)
+ {
+ if (System.Diagnostics.Debugger.IsAttached)
+ System.Diagnostics.Debugger.Break();
+ throw new Exception("Assertion failed: " + message);
+ }
+ }
+ public static void EndContractBlock()
+ { }
+ }
+}
+
+
+namespace Microsoft.Internal
+{
+ using System.Text;
+
+ internal static class Tuple
+ {
+ public static Tuple<T1> Create<T1>(T1 item1)
+ {
+ return new Tuple<T1>(item1);
+ }
+
+ public static Tuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2)
+ {
+ return new Tuple<T1, T2>(item1, item2);
+ }
+ }
+
+ [Serializable]
+ internal class Tuple<T1>
+ {
+ private readonly T1 m_Item1;
+
+ public T1 Item1 { get { return m_Item1; } }
+
+ public Tuple(T1 item1)
+ {
+ m_Item1 = item1;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("(");
+ sb.Append(m_Item1);
+ sb.Append(")");
+ return sb.ToString();
+ }
+
+ int Size
+ {
+ get
+ {
+ return 1;
+ }
+ }
+ }
+
+ [Serializable]
+ public class Tuple<T1, T2>
+ {
+ private readonly T1 m_Item1;
+ private readonly T2 m_Item2;
+
+ public T1 Item1 { get { return m_Item1; } }
+ public T2 Item2 { get { return m_Item2; } }
+
+ public Tuple(T1 item1, T2 item2)
+ {
+ m_Item1 = item1;
+ m_Item2 = item2;
+ }
+
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("(");
+ sb.Append(m_Item1);
+ sb.Append(", ");
+ sb.Append(m_Item2);
+ sb.Append(")");
+ return sb.ToString();
+ }
+
+ int Size
+ {
+ get
+ {
+ return 2;
+ }
+ }
+ }
+}
+
+#endif
+
+namespace Microsoft.Reflection
+{
+ using System.Reflection;
+
+#if ES_BUILD_PCL
+ [Flags]
+ public enum BindingFlags
+ {
+ DeclaredOnly = 0x02, // Only look at the members declared on the Type
+ Instance = 0x04, // Include Instance members in search
+ Static = 0x08, // Include Static members in search
+ Public = 0x10, // Include Public members in search
+ NonPublic = 0x20, // Include Non-Public members in search
+ }
+
+ public enum TypeCode {
+ Empty = 0, // Null reference
+ Object = 1, // Instance that isn't a value
+ DBNull = 2, // Database null value
+ Boolean = 3, // Boolean
+ Char = 4, // Unicode character
+ SByte = 5, // Signed 8-bit integer
+ Byte = 6, // Unsigned 8-bit integer
+ Int16 = 7, // Signed 16-bit integer
+ UInt16 = 8, // Unsigned 16-bit integer
+ Int32 = 9, // Signed 32-bit integer
+ UInt32 = 10, // Unsigned 32-bit integer
+ Int64 = 11, // Signed 64-bit integer
+ UInt64 = 12, // Unsigned 64-bit integer
+ Single = 13, // IEEE 32-bit float
+ Double = 14, // IEEE 64-bit double
+ Decimal = 15, // Decimal
+ DateTime = 16, // DateTime
+ String = 18, // Unicode character string
+ }
+#endif
+ static class ReflectionExtensions
+ {
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+
+ //
+ // Type extension methods
+ //
+ public static bool IsEnum(this Type type) { return type.IsEnum; }
+ public static bool IsAbstract(this Type type) { return type.IsAbstract; }
+ public static bool IsSealed(this Type type) { return type.IsSealed; }
+ public static bool IsValueType(this Type type) { return type.IsValueType; }
+ public static bool IsGenericType(this Type type) { return type.IsGenericType; }
+ public static Type BaseType(this Type type) { return type.BaseType; }
+ public static Assembly Assembly(this Type type) { return type.Assembly; }
+ public static TypeCode GetTypeCode(this Type type) { return Type.GetTypeCode(type); }
+
+ public static bool ReflectionOnly(this Assembly assm) { return assm.ReflectionOnly; }
+
+#else // ES_BUILD_PCL
+
+ //
+ // Type extension methods
+ //
+ public static bool IsEnum(this Type type) { return type.GetTypeInfo().IsEnum; }
+ public static bool IsAbstract(this Type type) { return type.GetTypeInfo().IsAbstract; }
+ public static bool IsSealed(this Type type) { return type.GetTypeInfo().IsSealed; }
+ public static bool IsValueType(this Type type) { return type.GetTypeInfo().IsValueType; }
+ public static bool IsGenericType(this Type type) { return type.IsConstructedGenericType; }
+ public static Type BaseType(this Type type) { return type.GetTypeInfo().BaseType; }
+ public static Assembly Assembly(this Type type) { return type.GetTypeInfo().Assembly; }
+ public static IEnumerable<PropertyInfo> GetProperties(this Type type)
+ {
+#if ES_BUILD_PN
+ return type.GetProperties();
+#else
+ return type.GetRuntimeProperties();
+#endif
+ }
+ public static MethodInfo GetGetMethod(this PropertyInfo propInfo) { return propInfo.GetMethod; }
+ public static Type[] GetGenericArguments(this Type type) { return type.GenericTypeArguments; }
+
+ public static MethodInfo[] GetMethods(this Type type, BindingFlags flags)
+ {
+ // Minimal implementation to cover only the cases we need
+ System.Diagnostics.Debug.Assert((flags & BindingFlags.DeclaredOnly) != 0);
+ System.Diagnostics.Debug.Assert((flags & ~(BindingFlags.DeclaredOnly|BindingFlags.Instance|BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic)) == 0);
+ Func<MethodInfo, bool> visFilter;
+ Func<MethodInfo, bool> instFilter;
+ switch (flags & (BindingFlags.Public | BindingFlags.NonPublic))
+ {
+ case 0: visFilter = mi => false; break;
+ case BindingFlags.Public: visFilter = mi => mi.IsPublic; break;
+ case BindingFlags.NonPublic: visFilter = mi => !mi.IsPublic; break;
+ default: visFilter = mi => true; break;
+ }
+ switch (flags & (BindingFlags.Instance | BindingFlags.Static))
+ {
+ case 0: instFilter = mi => false; break;
+ case BindingFlags.Instance: instFilter = mi => !mi.IsStatic; break;
+ case BindingFlags.Static: instFilter = mi => mi.IsStatic; break;
+ default: instFilter = mi => true; break;
+ }
+ List<MethodInfo> methodInfos = new List<MethodInfo>();
+ foreach (var declaredMethod in type.GetTypeInfo().DeclaredMethods)
+ {
+ if (visFilter(declaredMethod) && instFilter(declaredMethod))
+ methodInfos.Add(declaredMethod);
+ }
+ return methodInfos.ToArray();
+ }
+ public static FieldInfo[] GetFields(this Type type, BindingFlags flags)
+ {
+ // Minimal implementation to cover only the cases we need
+ System.Diagnostics.Debug.Assert((flags & BindingFlags.DeclaredOnly) != 0);
+ System.Diagnostics.Debug.Assert((flags & ~(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) == 0);
+ Func<FieldInfo, bool> visFilter;
+ Func<FieldInfo, bool> instFilter;
+ switch (flags & (BindingFlags.Public | BindingFlags.NonPublic))
+ {
+ case 0: visFilter = fi => false; break;
+ case BindingFlags.Public: visFilter = fi => fi.IsPublic; break;
+ case BindingFlags.NonPublic: visFilter = fi => !fi.IsPublic; break;
+ default: visFilter = fi => true; break;
+ }
+ switch (flags & (BindingFlags.Instance | BindingFlags.Static))
+ {
+ case 0: instFilter = fi => false; break;
+ case BindingFlags.Instance: instFilter = fi => !fi.IsStatic; break;
+ case BindingFlags.Static: instFilter = fi => fi.IsStatic; break;
+ default: instFilter = fi => true; break;
+ }
+ List<FieldInfo> fieldInfos = new List<FieldInfo>();
+ foreach (var declaredField in type.GetTypeInfo().DeclaredFields)
+ {
+ if (visFilter(declaredField) && instFilter(declaredField))
+ fieldInfos.Add(declaredField);
+ }
+ return fieldInfos.ToArray();
+ }
+ public static Type GetNestedType(this Type type, string nestedTypeName)
+ {
+ TypeInfo ti = null;
+ foreach(var nt in type.GetTypeInfo().DeclaredNestedTypes)
+ {
+ if (nt.Name == nestedTypeName)
+ {
+ ti = nt;
+ break;
+ }
+ }
+ return ti == null ? null : ti.AsType();
+ }
+ public static TypeCode GetTypeCode(this Type type)
+ {
+ if (type == typeof(bool)) return TypeCode.Boolean;
+ else if (type == typeof(byte)) return TypeCode.Byte;
+ else if (type == typeof(char)) return TypeCode.Char;
+ else if (type == typeof(ushort)) return TypeCode.UInt16;
+ else if (type == typeof(uint)) return TypeCode.UInt32;
+ else if (type == typeof(ulong)) return TypeCode.UInt64;
+ else if (type == typeof(sbyte)) return TypeCode.SByte;
+ else if (type == typeof(short)) return TypeCode.Int16;
+ else if (type == typeof(int)) return TypeCode.Int32;
+ else if (type == typeof(long)) return TypeCode.Int64;
+ else if (type == typeof(string)) return TypeCode.String;
+ else if (type == typeof(float)) return TypeCode.Single;
+ else if (type == typeof(double)) return TypeCode.Double;
+ else if (type == typeof(DateTime)) return TypeCode.DateTime;
+ else if (type == (typeof(Decimal))) return TypeCode.Decimal;
+ else return TypeCode.Object;
+ }
+
+ //
+ // FieldInfo extension methods
+ //
+ public static object GetRawConstantValue(this FieldInfo fi)
+ { return fi.GetValue(null); }
+
+ //
+ // Assembly extension methods
+ //
+ public static bool ReflectionOnly(this Assembly assm)
+ {
+ // In PCL we can't load in reflection-only context
+ return false;
+ }
+
+#endif
+ }
+}
+
+// Defining some no-ops in PCL builds
+#if ES_BUILD_PCL
+namespace System.Security
+{
+ class SuppressUnmanagedCodeSecurityAttribute : Attribute { }
+
+ enum SecurityAction { Demand }
+}
+
+namespace System.Security.Permissions
+{
+ class HostProtectionAttribute : Attribute { public bool MayLeakOnAbort { get; set; } }
+ class PermissionSetAttribute : Attribute
+ {
+ public PermissionSetAttribute(System.Security.SecurityAction action) { }
+ public bool Unrestricted { get; set; }
+ }
+}
+#endif
+
+#if ES_BUILD_PN
+namespace System
+{
+ internal static class AppDomain
+ {
+ public static int GetCurrentThreadId()
+ {
+ return Internal.Runtime.Augments.RuntimeThread.CurrentThread.ManagedThreadId;
+ }
+ }
+}
+#endif
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs
new file mode 100644
index 0000000000..5771354f67
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ArrayTypeInfo.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ internal sealed class ArrayTypeInfo : TraceLoggingTypeInfo
+ {
+ private readonly TraceLoggingTypeInfo elementInfo;
+
+ public ArrayTypeInfo(Type type, TraceLoggingTypeInfo elementInfo)
+ : base(type)
+ {
+ this.elementInfo = elementInfo;
+ }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ collector.BeginBufferedArray();
+ this.elementInfo.WriteMetadata(collector, name, format);
+ collector.EndBufferedArray();
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ var bookmark = collector.BeginBufferedArray();
+
+ var count = 0;
+ Array array = (Array)value.ReferenceValue;
+ if (array != null)
+ {
+ count = array.Length;
+ for (int i = 0; i < array.Length; i++)
+ {
+ this.elementInfo.WriteData(collector, elementInfo.PropertyValueFactory(array.GetValue(i)));
+ }
+ }
+
+ collector.EndBufferedArray(bookmark, count);
+ }
+
+ public override object GetData(object value)
+ {
+ var array = (Array)value;
+ var serializedArray = new object[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ {
+ serializedArray[i] = this.elementInfo.GetData(array.GetValue(i));
+ }
+ return serializedArray;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs
new file mode 100644
index 0000000000..76c01c6c06
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSet.cs
@@ -0,0 +1,127 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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 Interlocked = System.Threading.Interlocked;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: A very simple lock-free add-only dictionary.
+ /// Warning: this is a copy-by-value type. Copying performs a snapshot.
+ /// Accessing a readonly field always makes a copy of the field, so the
+ /// GetOrAdd method will not work as expected if called on a readonly field.
+ /// </summary>
+ /// <typeparam name="KeyType">
+ /// The type of the key, used for TryGet.
+ /// </typeparam>
+ /// <typeparam name="ItemType">
+ /// The type of the item, used for GetOrAdd.
+ /// </typeparam>
+ internal struct ConcurrentSet<KeyType, ItemType>
+ where ItemType : ConcurrentSetItem<KeyType, ItemType>
+ {
+ private ItemType[] items;
+
+ public ItemType TryGet(KeyType key)
+ {
+ ItemType item;
+ var oldItems = this.items;
+
+ if (oldItems != null)
+ {
+ var lo = 0;
+ var hi = oldItems.Length;
+ do
+ {
+ int i = (lo + hi) / 2;
+ item = oldItems[i];
+
+ int cmp = item.Compare(key);
+ if (cmp == 0)
+ {
+ goto Done;
+ }
+ else if (cmp < 0)
+ {
+ lo = i + 1;
+ }
+ else
+ {
+ hi = i;
+ }
+ }
+ while (lo != hi);
+ }
+
+ item = null;
+
+ Done:
+
+ return item;
+ }
+
+ public ItemType GetOrAdd(ItemType newItem)
+ {
+ ItemType item;
+ var oldItems = this.items;
+ ItemType[] newItems;
+
+ Retry:
+
+ if (oldItems == null)
+ {
+ newItems = new ItemType[] { newItem };
+ }
+ else
+ {
+ var lo = 0;
+ var hi = oldItems.Length;
+ do
+ {
+ int i = (lo + hi) / 2;
+ item = oldItems[i];
+
+ int cmp = item.Compare(newItem);
+ if (cmp == 0)
+ {
+ goto Done;
+ }
+ else if (cmp < 0)
+ {
+ lo = i + 1;
+ }
+ else
+ {
+ hi = i;
+ }
+ }
+ while (lo != hi);
+
+ int oldLength = oldItems.Length;
+ newItems = new ItemType[oldLength + 1];
+ Array.Copy(oldItems, 0, newItems, 0, lo);
+ newItems[lo] = newItem;
+ Array.Copy(oldItems, lo, newItems, lo + 1, oldLength - lo);
+ }
+
+ newItems = Interlocked.CompareExchange(ref this.items, newItems, oldItems);
+ if (oldItems != newItems)
+ {
+ oldItems = newItems;
+ goto Retry;
+ }
+
+ item = newItem;
+
+ Done:
+
+ return item;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs
new file mode 100644
index 0000000000..558dbf670b
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/ConcurrentSetItem.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: Abstract base class that must be inherited by items in a
+ /// ConcurrentSet.
+ /// </summary>
+ /// <typeparam name="KeyType">Type of the set's key.</typeparam>
+ /// <typeparam name="ItemType">Type of the derived class.</typeparam>
+ internal abstract class ConcurrentSetItem<KeyType, ItemType>
+ where ItemType : ConcurrentSetItem<KeyType, ItemType>
+ {
+ public abstract int Compare(ItemType other);
+ public abstract int Compare(KeyType key);
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs
new file mode 100644
index 0000000000..27aae820e9
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/DataCollector.cs
@@ -0,0 +1,318 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.InteropServices;
+using System.Security;
+
+#if ES_BUILD_STANDALONE
+using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: This is the implementation of the DataCollector
+ /// functionality. To enable safe access to the DataCollector from
+ /// untrusted code, there is one thread-local instance of this structure
+ /// per thread. The instance must be Enabled before any data is written to
+ /// it. The instance must be Finished before the data is passed to
+ /// EventWrite. The instance must be Disabled before the arrays referenced
+ /// by the pointers are freed or unpinned.
+ /// </summary>
+ internal unsafe struct DataCollector
+ {
+ [ThreadStatic]
+ internal static DataCollector ThreadInstance;
+
+ private byte* scratchEnd;
+ private EventSource.EventData* datasEnd;
+ private GCHandle* pinsEnd;
+ private EventSource.EventData* datasStart;
+ private byte* scratch;
+ private EventSource.EventData* datas;
+ private GCHandle* pins;
+ private byte[] buffer;
+ private int bufferPos;
+ private int bufferNesting; // We may merge many fields int a single blob. If we are doing this we increment this.
+ private bool writingScalars;
+
+ internal void Enable(
+ byte* scratch,
+ int scratchSize,
+ EventSource.EventData* datas,
+ int dataCount,
+ GCHandle* pins,
+ int pinCount)
+ {
+ this.datasStart = datas;
+ this.scratchEnd = scratch + scratchSize;
+ this.datasEnd = datas + dataCount;
+ this.pinsEnd = pins + pinCount;
+ this.scratch = scratch;
+ this.datas = datas;
+ this.pins = pins;
+ this.writingScalars = false;
+ }
+
+ internal void Disable()
+ {
+ this = new DataCollector();
+ }
+
+ /// <summary>
+ /// Completes the list of scalars. Finish must be called before the data
+ /// descriptor array is passed to EventWrite.
+ /// </summary>
+ /// <returns>
+ /// A pointer to the next unused data descriptor, or datasEnd if they were
+ /// all used. (Descriptors may be unused if a string or array was null.)
+ /// </returns>
+ internal EventSource.EventData* Finish()
+ {
+ this.ScalarsEnd();
+ return this.datas;
+ }
+
+ internal void AddScalar(void* value, int size)
+ {
+ var pb = (byte*)value;
+ if (this.bufferNesting == 0)
+ {
+ var scratchOld = this.scratch;
+ var scratchNew = scratchOld + size;
+ if (this.scratchEnd < scratchNew)
+ {
+ throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_AddScalarOutOfRange"));
+ }
+
+ this.ScalarsBegin();
+ this.scratch = scratchNew;
+
+ for (int i = 0; i != size; i++)
+ {
+ scratchOld[i] = pb[i];
+ }
+ }
+ else
+ {
+ var oldPos = this.bufferPos;
+ this.bufferPos = checked(this.bufferPos + size);
+ this.EnsureBuffer();
+ for (int i = 0; i != size; i++, oldPos++)
+ {
+ this.buffer[oldPos] = pb[i];
+ }
+ }
+ }
+
+ internal void AddBinary(string value, int size)
+ {
+ if (size > ushort.MaxValue)
+ {
+ size = ushort.MaxValue - 1;
+ }
+
+ if (this.bufferNesting != 0)
+ {
+ this.EnsureBuffer(size + 2);
+ }
+
+ this.AddScalar(&size, 2);
+
+ if (size != 0)
+ {
+ if (this.bufferNesting == 0)
+ {
+ this.ScalarsEnd();
+ this.PinArray(value, size);
+ }
+ else
+ {
+ var oldPos = this.bufferPos;
+ this.bufferPos = checked(this.bufferPos + size);
+ this.EnsureBuffer();
+ fixed (void* p = value)
+ {
+ Marshal.Copy((IntPtr)p, buffer, oldPos, size);
+ }
+ }
+ }
+ }
+
+ internal void AddBinary(Array value, int size)
+ {
+ this.AddArray(value, size, 1);
+ }
+
+ internal void AddArray(Array value, int length, int itemSize)
+ {
+ if (length > ushort.MaxValue)
+ {
+ length = ushort.MaxValue;
+ }
+
+ var size = length * itemSize;
+ if (this.bufferNesting != 0)
+ {
+ this.EnsureBuffer(size + 2);
+ }
+
+ this.AddScalar(&length, 2);
+
+ if (length != 0)
+ {
+ if (this.bufferNesting == 0)
+ {
+ this.ScalarsEnd();
+ this.PinArray(value, size);
+ }
+ else
+ {
+ var oldPos = this.bufferPos;
+ this.bufferPos = checked(this.bufferPos + size);
+ this.EnsureBuffer();
+ Buffer.BlockCopy(value, 0, this.buffer, oldPos, size);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Marks the start of a non-blittable array or enumerable.
+ /// </summary>
+ /// <returns>Bookmark to be passed to EndBufferedArray.</returns>
+ internal int BeginBufferedArray()
+ {
+ this.BeginBuffered();
+ this.bufferPos += 2; // Reserve space for the array length (filled in by EndEnumerable)
+ return this.bufferPos;
+ }
+
+ /// <summary>
+ /// Marks the end of a non-blittable array or enumerable.
+ /// </summary>
+ /// <param name="bookmark">The value returned by BeginBufferedArray.</param>
+ /// <param name="count">The number of items in the array.</param>
+ internal void EndBufferedArray(int bookmark, int count)
+ {
+ this.EnsureBuffer();
+ this.buffer[bookmark - 2] = unchecked((byte)count);
+ this.buffer[bookmark - 1] = unchecked((byte)(count >> 8));
+ this.EndBuffered();
+ }
+
+ /// <summary>
+ /// Marks the start of dynamically-buffered data.
+ /// </summary>
+ internal void BeginBuffered()
+ {
+ this.ScalarsEnd();
+ this.bufferNesting += 1;
+ }
+
+ /// <summary>
+ /// Marks the end of dynamically-buffered data.
+ /// </summary>
+ internal void EndBuffered()
+ {
+ this.bufferNesting -= 1;
+
+ if (this.bufferNesting == 0)
+ {
+ /*
+ TODO (perf): consider coalescing adjacent buffered regions into a
+ single buffer, similar to what we're already doing for adjacent
+ scalars. In addition, if a type contains a buffered region adjacent
+ to a blittable array, and the blittable array is small, it would be
+ more efficient to buffer the array instead of pinning it.
+ */
+
+ this.EnsureBuffer();
+ this.PinArray(this.buffer, this.bufferPos);
+ this.buffer = null;
+ this.bufferPos = 0;
+ }
+ }
+
+ private void EnsureBuffer()
+ {
+ var required = this.bufferPos;
+ if (this.buffer == null || this.buffer.Length < required)
+ {
+ this.GrowBuffer(required);
+ }
+ }
+
+ private void EnsureBuffer(int additionalSize)
+ {
+ var required = this.bufferPos + additionalSize;
+ if (this.buffer == null || this.buffer.Length < required)
+ {
+ this.GrowBuffer(required);
+ }
+ }
+
+ private void GrowBuffer(int required)
+ {
+ var newSize = this.buffer == null ? 64 : this.buffer.Length;
+
+ do
+ {
+ newSize *= 2;
+ }
+ while (newSize < required);
+
+ Array.Resize(ref this.buffer, newSize);
+ }
+
+ private void PinArray(object value, int size)
+ {
+ var pinsTemp = this.pins;
+ if (this.pinsEnd <= pinsTemp)
+ {
+ throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_PinArrayOutOfRange"));
+ }
+
+ var datasTemp = this.datas;
+ if (this.datasEnd <= datasTemp)
+ {
+ throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_DataDescriptorsOutOfRange"));
+ }
+
+ this.pins = pinsTemp + 1;
+ this.datas = datasTemp + 1;
+
+ *pinsTemp = GCHandle.Alloc(value, GCHandleType.Pinned);
+ datasTemp->m_Ptr = (long)(ulong)(UIntPtr)(void*)pinsTemp->AddrOfPinnedObject();
+ datasTemp->m_Size = size;
+ }
+
+ private void ScalarsBegin()
+ {
+ if (!this.writingScalars)
+ {
+ var datasTemp = this.datas;
+ if (this.datasEnd <= datasTemp)
+ {
+ throw new IndexOutOfRangeException(Resources.GetResourceString("EventSource_DataDescriptorsOutOfRange"));
+ }
+
+ datasTemp->m_Ptr = (long)(ulong)(UIntPtr)this.scratch;
+ this.writingScalars = true;
+ }
+ }
+
+ private void ScalarsEnd()
+ {
+ if (this.writingScalars)
+ {
+ var datasTemp = this.datas;
+ datasTemp->m_Size = checked((int)(this.scratch - (byte*)datasTemp->m_Ptr));
+ this.datas = datasTemp + 1;
+ this.writingScalars = false;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs
new file mode 100644
index 0000000000..bc7fb8c346
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EmptyStruct.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: Empty struct indicating no payload data.
+ /// </summary>
+ internal struct EmptyStruct
+ {
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs
new file mode 100644
index 0000000000..7a23378bb1
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumHelper.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#if EVENTSOURCE_GENERICS
+?using System;
+using System.Reflection;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Provides support for casting enums to their underlying type
+ /// from within generic context.
+ /// </summary>
+ /// <typeparam name="UnderlyingType">
+ /// The underlying type of the enum.
+ /// </typeparam>
+ internal static class EnumHelper<UnderlyingType>
+ {
+ public static UnderlyingType Cast<ValueType>(ValueType value)
+ {
+ return (UnderlyingType)(object)value;
+ }
+ }
+
+}
+#endif
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs
new file mode 100644
index 0000000000..74a3fa27b2
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EnumerableTypeInfo.cs
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+using System.Collections.Generic;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ internal sealed class EnumerableTypeInfo : TraceLoggingTypeInfo
+ {
+ private readonly TraceLoggingTypeInfo elementInfo;
+
+ public EnumerableTypeInfo(Type type, TraceLoggingTypeInfo elementInfo)
+ : base(type)
+ {
+ this.elementInfo = elementInfo;
+ }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ collector.BeginBufferedArray();
+ this.elementInfo.WriteMetadata(collector, name, format);
+ collector.EndBufferedArray();
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ var bookmark = collector.BeginBufferedArray();
+
+ var count = 0;
+ IEnumerable enumerable = (IEnumerable)value.ReferenceValue;
+ if (enumerable != null)
+ {
+ foreach (var element in enumerable)
+ {
+ this.elementInfo.WriteData(collector, elementInfo.PropertyValueFactory(element));
+ count++;
+ }
+ }
+
+ collector.EndBufferedArray(bookmark, count);
+ }
+
+ public override object GetData(object value)
+ {
+ var iterType = (IEnumerable)value;
+ List<object> serializedEnumerable = new List<object>();
+ foreach (var element in iterType)
+ {
+ serializedEnumerable.Add(elementInfo.GetData(element));
+ }
+ return serializedEnumerable.ToArray();
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs
new file mode 100644
index 0000000000..cdedf13c64
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventDataAttribute.cs
@@ -0,0 +1,146 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Used when authoring types that will be passed to EventSource.Write.
+ /// EventSource.Write&lt;T> only works when T is either an anonymous type
+ /// or a type with an [EventData] attribute. In addition, the properties
+ /// of T must be supported property types. Supported property types include
+ /// simple built-in types (int, string, Guid, DateTime, DateTimeOffset,
+ /// KeyValuePair, etc.), anonymous types that only contain supported types,
+ /// types with an [EventData] attribute, arrays of the above, and IEnumerable
+ /// of the above.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
+ public class EventDataAttribute
+ : Attribute
+ {
+ private EventLevel level = (EventLevel)(-1);
+ private EventOpcode opcode = (EventOpcode)(-1);
+
+ /// <summary>
+ /// Gets or sets the name to use if this type is used for an
+ /// implicitly-named event or an implicitly-named property.
+ ///
+ /// Example 1:
+ ///
+ /// EventSource.Write(null, new T()); // implicitly-named event
+ ///
+ /// The name of the event will be determined as follows:
+ ///
+ /// if (T has an EventData attribute and attribute.Name != null)
+ /// eventName = attribute.Name;
+ /// else
+ /// eventName = typeof(T).Name;
+ ///
+ /// Example 2:
+ ///
+ /// EventSource.Write(name, new { _1 = new T() }); // implicitly-named field
+ ///
+ /// The name of the field will be determined as follows:
+ ///
+ /// if (T has an EventData attribute and attribute.Name != null)
+ /// fieldName = attribute.Name;
+ /// else
+ /// fieldName = typeof(T).Name;
+ /// </summary>
+ public string Name
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Gets or sets the level to use for the event.
+ /// Invalid levels (outside the range 0..255) are treated as unset.
+ /// Note that the Level attribute can bubble-up, i.e. if a type contains
+ /// a sub-object (a field or property), and the sub-object's type has a
+ /// TraceLoggingEvent attribute, the Level from the sub-object's attribute
+ /// can affect the event's level.
+ ///
+ /// Example: for EventSource.Write(name, options, data), the level of the
+ /// event will be determined as follows:
+ ///
+ /// if (options.Level has been set)
+ /// eventLevel = options.Level;
+ /// else if (data.GetType() has a TraceLoggingEvent attribute and attribute.Level has been set)
+ /// eventLevel = attribute.Level;
+ /// else if (a field/property contained in data has a TraceLoggingEvent attribute and attribute.Level has been set)
+ /// eventLevel = attribute.Level;
+ /// else
+ /// eventLevel = EventLevel.LogAlways;
+ /// </summary>
+ internal EventLevel Level
+ {
+ get { return this.level; }
+ set { this.level = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the opcode to use for the event.
+ /// Invalid opcodes (outside the range 0..255) are treated as unset.
+ /// Note that the Opcode attribute can bubble-up, i.e. if a type contains
+ /// a sub-object (a field or property), and the sub-object's type has a
+ /// TraceLoggingEvent attribute, the Opcode from the sub-object's attribute
+ /// can affect the event's opcode.
+ ///
+ /// Example: for EventSource.Write(name, options, data), the opcode of the
+ /// event will be determined as follows:
+ ///
+ /// if (options.Opcode has been set)
+ /// eventOpcode = options.Opcode;
+ /// else if (data.GetType() has a TraceLoggingEvent attribute and attribute.Opcode has been set)
+ /// eventOpcode = attribute.Opcode;
+ /// else if (a field/property contained in data has a TraceLoggingEvent attribute and attribute.Opcode has been set)
+ /// eventOpcode = attribute.Opcode;
+ /// else
+ /// eventOpcode = EventOpcode.Info;
+ /// </summary>
+ internal EventOpcode Opcode
+ {
+ get { return this.opcode; }
+ set { this.opcode = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the keywords to use for the event.
+ /// Note that the Keywords attribute can bubble-up, i.e. if a type contains
+ /// a sub-object (a field or property), and the sub-object's type has a
+ /// TraceLoggingEvent attribute, the Keywords from the sub-object's attribute
+ /// can affect the event's keywords.
+ ///
+ /// Example: for EventSource.Write(name, options, data), the keywords of the
+ /// event will be determined as follows:
+ ///
+ /// eventKeywords = options.Keywords;
+ /// if (data.GetType() has a TraceLoggingEvent attribute)
+ /// eventKeywords |= attribute.Keywords;
+ /// if (a field/property contained in data has a TraceLoggingEvent attribute)
+ /// eventKeywords |= attribute.Keywords;
+ /// </summary>
+ internal EventKeywords Keywords
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Gets or sets the flags for an event. These flags are ignored by ETW,
+ /// but can have meaning to the event consumer.
+ /// </summary>
+ internal EventTags Tags
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs
new file mode 100644
index 0000000000..1a298c2851
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldAttribute.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Tags are flags that are not interpreted by EventSource but are passed along
+ /// to the EventListener. The EventListener determines the semantics of the flags.
+ /// </summary>
+ [Flags]
+ public enum EventFieldTags
+ {
+ /// <summary>
+ /// No special traits are added to the field.
+ /// </summary>
+ None = 0,
+
+ /* Bits below 0x10000 are available for any use by the provider. */
+ /* Bits at or above 0x10000 are reserved for definition by Microsoft. */
+ }
+
+ /// <summary>
+ /// TraceLogging: used when authoring types that will be passed to EventSource.Write.
+ /// Controls how a field or property is handled when it is written as a
+ /// field in a TraceLogging event. Apply this attribute to a field or
+ /// property if the default handling is not correct. (Apply the
+ /// TraceLoggingIgnore attribute if the property should not be
+ /// included as a field in the event.)
+ /// The default for Name is null, which means that the name of the
+ /// underlying field or property will be used as the event field's name.
+ /// The default for PiiTag is 0, which means that the event field does not
+ /// contain personally-identifiable information.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property)]
+ public class EventFieldAttribute
+ : Attribute
+ {
+ /// <summary>
+ /// User defined options for the field. These are not interpreted by the EventSource
+ /// but are available to the Listener. See EventFieldSettings for details
+ /// </summary>
+ public EventFieldTags Tags
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Gets or sets the name to use for the field. This defaults to null.
+ /// If null, the name of the corresponding property will be used
+ /// as the event field's name.
+ /// TODO REMOVE
+ /// </summary>
+ internal string Name
+ {
+ get;
+ set;
+ }
+
+ /// <summary>
+ /// Gets or sets a field formatting hint.
+ /// </summary>
+ public EventFieldFormat Format
+ {
+ get;
+ set;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs
new file mode 100644
index 0000000000..fd77b07965
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventFieldFormat.cs
@@ -0,0 +1,130 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Provides a hint that may be used by an event listener when formatting
+ /// an event field for display. Note that the event listener may ignore the
+ /// hint if it does not recognize a particular combination of type and format.
+ /// Similar to TDH_OUTTYPE.
+ /// </summary>
+ public enum EventFieldFormat
+ {
+ /// <summary>
+ /// Field receives default formatting based on the field's underlying type.
+ /// </summary>
+ Default = 0,
+#if false
+ /// <summary>
+ /// Field should not be displayed.
+ /// </summary>
+ NoPrint = 1,
+#endif
+ /// <summary>
+ /// Field should be formatted as character or string data.
+ /// Typically applied to 8-bit or 16-bit integers.
+ /// This is the default format for String and Char types.
+ /// </summary>
+ String = 2,
+
+ /// <summary>
+ /// Field should be formatted as boolean data. Typically applied to 8-bit
+ /// or 32-bit integers. This is the default format for the Boolean type.
+ /// </summary>
+ Boolean = 3,
+
+ /// <summary>
+ /// Field should be formatted as hexadecimal data. Typically applied to
+ /// integer types.
+ /// </summary>
+ Hexadecimal = 4,
+
+#if false
+ /// <summary>
+ /// Field should be formatted as a process identifier. Typically applied to
+ /// 32-bit integer types.
+ /// </summary>
+ ProcessId = 5,
+
+ /// <summary>
+ /// Field should be formatted as a thread identifier. Typically applied to
+ /// 32-bit integer types.
+ /// </summary>
+ ThreadId = 6,
+
+ /// <summary>
+ /// Field should be formatted as an Internet port. Typically applied to 16-bit integer
+ /// types.
+ /// </summary>
+ Port = 7,
+ /// <summary>
+ /// Field should be formatted as an Internet Protocol v4 address. Typically applied to
+ /// 32-bit integer types.
+ /// </summary>
+ Ipv4Address = 8,
+
+ /// <summary>
+ /// Field should be formatted as an Internet Protocol v6 address. Typically applied to
+ /// byte[] types.
+ /// </summary>
+ Ipv6Address = 9,
+ /// <summary>
+ /// Field should be formatted as a SOCKADDR. Typically applied to byte[] types.
+ /// </summary>
+ SocketAddress = 10,
+#endif
+ /// <summary>
+ /// Field should be formatted as XML string data. Typically applied to
+ /// strings or arrays of 8-bit or 16-bit integers.
+ /// </summary>
+ Xml = 11,
+
+ /// <summary>
+ /// Field should be formatted as JSON string data. Typically applied to
+ /// strings or arrays of 8-bit or 16-bit integers.
+ /// </summary>
+ Json = 12,
+#if false
+ /// <summary>
+ /// Field should be formatted as a Win32 error code. Typically applied to
+ /// 32-bit integer types.
+ /// </summary>
+ Win32Error = 13,
+
+ /// <summary>
+ /// Field should be formatted as an NTSTATUS code. Typically applied to
+ /// 32-bit integer types.
+ /// </summary>
+ NTStatus = 14,
+#endif
+ /// <summary>
+ /// Field should be formatted as an HRESULT code. Typically applied to
+ /// 32-bit integer types.
+ /// </summary>
+ HResult = 15,
+#if false
+ /// <summary>
+ /// Field should be formatted as a FILETIME. Typically applied to 64-bit
+ /// integer types. This is the default format for DateTime types.
+ /// </summary>
+ FileTime = 16,
+ /// <summary>
+ /// When applied to a numeric type, indicates that the type should be formatted
+ /// as a signed integer. This is the default format for signed integer types.
+ /// </summary>
+ Signed = 17,
+
+ /// <summary>
+ /// When applied to a numeric type, indicates that the type should be formatted
+ /// as an unsigned integer. This is the default format for unsigned integer types.
+ /// </summary>
+ Unsigned = 18,
+#endif
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs
new file mode 100644
index 0000000000..769345f78e
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventIgnoreAttribute.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Used when authoring types that will be passed to EventSource.Write.
+ /// By default, EventSource.Write will write all of an object's public
+ /// properties to the event payload. Apply [EventIgnore] to a public
+ /// property to prevent EventSource.Write from including the property in
+ /// the event.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property)]
+ public class EventIgnoreAttribute
+ : Attribute
+ {
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs
new file mode 100644
index 0000000000..5967ad6ab5
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventPayload.cs
@@ -0,0 +1,155 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+using System.Collections;
+using System.Diagnostics;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// EventPayload class holds the list of parameters and their corresponding values for user defined types passed to
+ /// EventSource APIs.
+ /// Preserving the order of the elements as they were found inside user defined types is the most important characteristic of this class.
+ /// </summary>
+ internal class EventPayload : IDictionary<string, object>
+ {
+ internal EventPayload(List<string> payloadNames, List<object> payloadValues)
+ {
+ Debug.Assert(payloadNames.Count == payloadValues.Count);
+
+ m_names = payloadNames;
+ m_values = payloadValues;
+ }
+
+ public ICollection<string> Keys { get { return m_names; } }
+ public ICollection<object> Values { get { return m_values; } }
+
+ public object this[string key]
+ {
+ get
+ {
+ if (key == null)
+ throw new System.ArgumentNullException(nameof(key));
+
+ int position = 0;
+ foreach(var name in m_names)
+ {
+ if (name == key)
+ {
+ return m_values[position];
+ }
+ position++;
+ }
+
+ throw new System.Collections.Generic.KeyNotFoundException();
+ }
+ set
+ {
+ throw new System.NotSupportedException();
+ }
+ }
+
+ public void Add(string key, object value)
+ {
+ throw new System.NotSupportedException();
+ }
+
+ public void Add(KeyValuePair<string, object> payloadEntry)
+ {
+ throw new System.NotSupportedException();
+ }
+
+ public void Clear()
+ {
+ throw new System.NotSupportedException();
+ }
+
+ public bool Contains(KeyValuePair<string, object> entry)
+ {
+ return ContainsKey(entry.Key);
+ }
+
+ public bool ContainsKey(string key)
+ {
+ if (key == null)
+ throw new System.ArgumentNullException(nameof(key));
+
+ foreach (var item in m_names)
+ {
+ if (item == key)
+ return true;
+ }
+ return false;
+ }
+
+ public int Count { get { return m_names.Count; } }
+
+ public bool IsReadOnly { get { return true; } }
+
+ public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
+ {
+ for (int i = 0; i < Keys.Count; i++)
+ {
+ yield return new KeyValuePair<string, object>(this.m_names[i], this.m_values[i]);
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ var instance = this as IEnumerable<KeyValuePair<string, object>>;
+ return instance.GetEnumerator();
+ }
+
+ public void CopyTo(KeyValuePair<string, object>[] payloadEntries, int count)
+ {
+ throw new System.NotSupportedException();
+ }
+
+ public bool Remove(string key)
+ {
+ throw new System.NotSupportedException();
+ }
+
+ public bool Remove(KeyValuePair<string, object> entry)
+ {
+ throw new System.NotSupportedException();
+ }
+
+ public bool TryGetValue(string key, out object value)
+ {
+ if (key == null)
+ throw new System.ArgumentNullException(nameof(key));
+
+ int position = 0;
+ foreach (var name in m_names)
+ {
+ if (name == key)
+ {
+ value = m_values[position];
+ return true;
+ }
+ position++;
+ }
+
+ value = default(object);
+ return false;
+ }
+
+ #region private
+ private List<string> m_names;
+ private List<object> m_values;
+ #endregion
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs
new file mode 100644
index 0000000000..38c1767462
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceActivity.cs
@@ -0,0 +1,321 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Provides support for EventSource activities by marking the start and
+ /// end of a particular operation.
+ /// </summary>
+ internal sealed class EventSourceActivity
+ : IDisposable
+ {
+ /// <summary>
+ /// Initializes a new instance of the EventSourceActivity class that
+ /// is attached to the specified event source. The new activity will
+ /// not be attached to any related (parent) activity.
+ /// The activity is created in the Initialized state.
+ /// </summary>
+ /// <param name="eventSource">
+ /// The event source to which the activity information is written.
+ /// </param>
+ public EventSourceActivity(EventSource eventSource)
+ {
+ if (eventSource == null)
+ throw new ArgumentNullException(nameof(eventSource));
+ Contract.EndContractBlock();
+
+ this.eventSource = eventSource;
+ }
+
+ /// <summary>
+ /// You can make an activity out of just an EventSource.
+ /// </summary>
+ public static implicit operator EventSourceActivity(EventSource eventSource) { return new EventSourceActivity(eventSource); }
+
+ /* Properties */
+ /// <summary>
+ /// Gets the event source to which this activity writes events.
+ /// </summary>
+ public EventSource EventSource
+ {
+ get { return this.eventSource; }
+ }
+
+ /// <summary>
+ /// Gets this activity's unique identifier, or the default Guid if the
+ /// event source was disabled when the activity was initialized.
+ /// </summary>
+ public Guid Id
+ {
+ get { return this.activityId; }
+ }
+
+#if false // don't expose RelatedActivityId unless there is a need.
+ /// <summary>
+ /// Gets the unique identifier of this activity's related (parent)
+ /// activity.
+ /// </summary>
+ public Guid RelatedId
+ {
+ get { return this.relatedActivityId; }
+ }
+#endif
+
+ /// <summary>
+ /// Writes a Start event with the specified name and data. If the start event is not active (because the provider
+ /// is not on or keyword-level indiates the event is off, then the returned activity is simply the 'this' poitner
+ /// and it is effectively like the Start d
+ ///
+ /// A new activityID GUID is generated and the returned
+ /// EventSourceActivity remembers this activity and will mark every event (including the start stop and any writes)
+ /// with this activityID. In addition the Start activity will log a 'relatedActivityID' that was the activity
+ /// ID before the start event. This way event processors can form a linked list of all the activities that
+ /// caused this one (directly or indirectly).
+ /// </summary>
+ /// <param name="eventName">
+ /// The name to use for the event. It is strongly suggested that this name end in 'Start' (e.g. DownloadStart).
+ /// If you do this, then the Stop() method will automatically replace the 'Start' suffix with a 'Stop' suffix.
+ /// </param>
+ /// <param name="options">Allow options (keywords, level) to be set for the write associated with this start
+ /// These will also be used for the stop event.</param>
+ /// <param name="data">The data to include in the event.</param>
+ public EventSourceActivity Start<T>(string eventName, EventSourceOptions options, T data)
+ {
+ return this.Start(eventName, ref options, ref data);
+ }
+ /// <summary>
+ /// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords
+ /// and level==Info) Data payload is empty.
+ /// </summary>
+ public EventSourceActivity Start(string eventName)
+ {
+ var options = new EventSourceOptions();
+ var data = new EmptyStruct();
+ return this.Start(eventName, ref options, ref data);
+ }
+ /// <summary>
+ /// Shortcut version see Start(string eventName, EventSourceOptions options, T data). Data payload is empty.
+ /// </summary>
+ public EventSourceActivity Start(string eventName, EventSourceOptions options)
+ {
+ var data = new EmptyStruct();
+ return this.Start(eventName, ref options, ref data);
+ }
+ /// <summary>
+ /// Shortcut version see Start(string eventName, EventSourceOptions options, T data) Options is empty (no keywords
+ /// and level==Info)
+ /// </summary>
+ public EventSourceActivity Start<T>(string eventName, T data)
+ {
+ var options = new EventSourceOptions();
+ return this.Start(eventName, ref options, ref data);
+ }
+
+ /// <summary>
+ /// Writes a Stop event with the specified data, and sets the activity
+ /// to the Stopped state. The name is determined by the eventName used in Start.
+ /// If that Start event name is suffixed with 'Start' that is removed, and regardless
+ /// 'Stop' is appended to the result to form the Stop event name.
+ /// May only be called when the activity is in the Started state.
+ /// </summary>
+ /// <param name="data">The data to include in the event.</param>
+ public void Stop<T>(T data)
+ {
+ this.Stop(null, ref data);
+ }
+ /// <summary>
+ /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop')
+ /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that
+ /// you start with the same prefix used for the start event and you end with the 'Stop' suffix.
+ /// </summary>
+ public void Stop<T>(string eventName)
+ {
+ var data = new EmptyStruct();
+ this.Stop(eventName, ref data);
+ }
+ /// <summary>
+ /// Used if you wish to use the non-default stop name (which is the start name with Start replace with 'Stop')
+ /// This can be useful to indicate unusual ways of stoping (but it is still STRONGLY recommeded that
+ /// you start with the same prefix used for the start event and you end with the 'Stop' suffix.
+ /// </summary>
+ public void Stop<T>(string eventName, T data)
+ {
+ this.Stop(eventName, ref data);
+ }
+
+ /// <summary>
+ /// Writes an event associated with this activity to the eventSource associted with this activity.
+ /// May only be called when the activity is in the Started state.
+ /// </summary>
+ /// <param name="eventName">
+ /// The name to use for the event. If null, the name is determined from
+ /// data's type.
+ /// </param>
+ /// <param name="options">
+ /// The options to use for the event.
+ /// </param>
+ /// <param name="data">The data to include in the event.</param>
+ public void Write<T>(string eventName, EventSourceOptions options, T data)
+ {
+ this.Write(this.eventSource, eventName, ref options, ref data);
+ }
+ /// <summary>
+ /// Writes an event associated with this activity.
+ /// May only be called when the activity is in the Started state.
+ /// </summary>
+ /// <param name="eventName">
+ /// The name to use for the event. If null, the name is determined from
+ /// data's type.
+ /// </param>
+ /// <param name="data">The data to include in the event.</param>
+ public void Write<T>(string eventName, T data)
+ {
+ var options = new EventSourceOptions();
+ this.Write(this.eventSource, eventName, ref options, ref data);
+ }
+ /// <summary>
+ /// Writes a trivial event associated with this activity.
+ /// May only be called when the activity is in the Started state.
+ /// </summary>
+ /// <param name="eventName">
+ /// The name to use for the event. Must not be null.
+ /// </param>
+ /// <param name="options">
+ /// The options to use for the event.
+ /// </param>
+ public void Write(string eventName, EventSourceOptions options)
+ {
+ var data = new EmptyStruct();
+ this.Write(this.eventSource, eventName, ref options, ref data);
+ }
+ /// <summary>
+ /// Writes a trivial event associated with this activity.
+ /// May only be called when the activity is in the Started state.
+ /// </summary>
+ /// <param name="eventName">
+ /// The name to use for the event. Must not be null.
+ /// </param>
+ public void Write(string eventName)
+ {
+ var options = new EventSourceOptions();
+ var data = new EmptyStruct();
+ this.Write(this.eventSource, eventName, ref options, ref data);
+ }
+ /// <summary>
+ /// Writes an event to a arbitrary eventSource stamped with the activity ID of this activity.
+ /// </summary>
+ public void Write<T>(EventSource source, string eventName, EventSourceOptions options, T data)
+ {
+ this.Write(source, eventName, ref options, ref data);
+ }
+
+ /// <summary>
+ /// Releases any unmanaged resources associated with this object.
+ /// If the activity is in the Started state, calls Stop().
+ /// </summary>
+ public void Dispose()
+ {
+ if (this.state == State.Started)
+ {
+ var data = new EmptyStruct();
+ this.Stop(null, ref data);
+ }
+ }
+
+ #region private
+ private EventSourceActivity Start<T>(string eventName, ref EventSourceOptions options, ref T data)
+ {
+ if (this.state != State.Started)
+ throw new InvalidOperationException();
+
+ // If the source is not on at all, then we don't need to do anything and we can simply return ourselves.
+ if (!this.eventSource.IsEnabled())
+ return this;
+
+ var newActivity = new EventSourceActivity(eventSource);
+ if (!this.eventSource.IsEnabled(options.Level, options.Keywords))
+ {
+ // newActivity.relatedActivityId = this.Id;
+ Guid relatedActivityId = this.Id;
+ newActivity.activityId = Guid.NewGuid();
+ newActivity.startStopOptions = options;
+ newActivity.eventName = eventName;
+ newActivity.startStopOptions.Opcode = EventOpcode.Start;
+ this.eventSource.Write(eventName, ref newActivity.startStopOptions, ref newActivity.activityId, ref relatedActivityId, ref data);
+ }
+ else
+ {
+ // If we are not active, we don't set the eventName, which basically also turns off the Stop event as well.
+ newActivity.activityId = this.Id;
+ }
+
+ return newActivity;
+ }
+
+ private void Write<T>(EventSource eventSource, string eventName, ref EventSourceOptions options, ref T data)
+ {
+ if (this.state != State.Started)
+ throw new InvalidOperationException(); // Write after stop.
+ if (eventName == null)
+ throw new ArgumentNullException();
+
+ eventSource.Write(eventName, ref options, ref this.activityId, ref s_empty, ref data);
+ }
+
+ private void Stop<T>(string eventName, ref T data)
+ {
+ if (this.state != State.Started)
+ throw new InvalidOperationException();
+
+ // If start was not fired, then stop isn't as well.
+ if (!StartEventWasFired)
+ return;
+
+ this.state = State.Stopped;
+ if (eventName == null)
+ {
+ eventName = this.eventName;
+ if (eventName.EndsWith("Start"))
+ eventName = eventName.Substring(0, eventName.Length - 5);
+ eventName = eventName + "Stop";
+ }
+ this.startStopOptions.Opcode = EventOpcode.Stop;
+ this.eventSource.Write(eventName, ref this.startStopOptions, ref this.activityId, ref s_empty, ref data);
+ }
+
+ private enum State
+ {
+ Started,
+ Stopped
+ }
+
+ /// <summary>
+ /// If eventName is non-null then we logged a start event
+ /// </summary>
+ private bool StartEventWasFired { get { return eventName != null; } }
+
+ private readonly EventSource eventSource;
+ private EventSourceOptions startStopOptions;
+ internal Guid activityId;
+ // internal Guid relatedActivityId;
+ private State state;
+ private string eventName;
+
+ static internal Guid s_empty;
+ #endregion
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs
new file mode 100644
index 0000000000..26305a5708
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/EventSourceOptions.cs
@@ -0,0 +1,130 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Used when calling EventSource.Write.
+ /// Optional overrides for event settings such as Level, Keywords, or Opcode.
+ /// If overrides are not provided for a setting, default values will be used.
+ /// </summary>
+ public struct EventSourceOptions
+ {
+ internal EventKeywords keywords;
+ internal EventTags tags;
+ internal EventActivityOptions activityOptions;
+ internal byte level;
+ internal byte opcode;
+ internal byte valuesSet;
+
+ internal const byte keywordsSet = 0x1;
+ internal const byte tagsSet = 0x2;
+ internal const byte levelSet = 0x4;
+ internal const byte opcodeSet = 0x8;
+ internal const byte activityOptionsSet = 0x10;
+
+ /// <summary>
+ /// Gets or sets the level to use for the specified event. If this property
+ /// is unset, the event's level will be 5 (Verbose).
+ /// </summary>
+ public EventLevel Level
+ {
+ get
+ {
+ return (EventLevel)this.level;
+ }
+
+ set
+ {
+ this.level = checked((byte)value);
+ this.valuesSet |= levelSet;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the opcode to use for the specified event. If this property
+ /// is unset, the event's opcode will 0 (Info).
+ /// </summary>
+ public EventOpcode Opcode
+ {
+ get
+ {
+ return (EventOpcode)this.opcode;
+ }
+
+ set
+ {
+ this.opcode = checked((byte)value);
+ this.valuesSet |= opcodeSet;
+ }
+ }
+
+ internal bool IsOpcodeSet
+ {
+ get
+ {
+ return (this.valuesSet & opcodeSet) != 0;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the keywords to use for the specified event. If this
+ /// property is unset, the event's keywords will be 0.
+ /// </summary>
+ public EventKeywords Keywords
+ {
+ get
+ {
+ return this.keywords;
+ }
+
+ set
+ {
+ this.keywords = value;
+ this.valuesSet |= keywordsSet;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the tags to use for the specified event. If this property is
+ /// unset, the event's tags will be 0.
+ /// </summary>
+ public EventTags Tags
+ {
+ get
+ {
+ return this.tags;
+ }
+
+ set
+ {
+ this.tags = value;
+ this.valuesSet |= tagsSet;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the activity options for this specified events. If this property is
+ /// unset, the event's activity options will be 0.
+ /// </summary>
+ public EventActivityOptions ActivityOptions
+ {
+ get
+ {
+ return this.activityOptions;
+ }
+ set
+ {
+ this.activityOptions = value;
+ this.valuesSet |= activityOptionsSet;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs
new file mode 100644
index 0000000000..309226b84d
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/FieldMetadata.cs
@@ -0,0 +1,231 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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 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(
+ nameof(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(Resources.GetResourceString("EventSource_NotSupportedArrayOfNil"));
+ }
+ if (coreType == (int)TraceLoggingDataType.Binary)
+ {
+ throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedArrayOfBinary"));
+ }
+#if !BROKEN_UNTIL_M3
+ if (coreType == (int)TraceLoggingDataType.Utf16String ||
+ coreType == (int)TraceLoggingDataType.MbcsString)
+ {
+ throw new NotSupportedException(Resources.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(Resources.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;
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs
new file mode 100644
index 0000000000..3e5997bc9b
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/InvokeTypeInfo.cs
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: An implementation of TraceLoggingTypeInfo that works
+ /// for arbitrary types. It writes all public instance properties of
+ /// the type.
+ /// </summary>
+ /// <typeparam name="ContainerType">
+ /// Type from which to read values.
+ /// </typeparam>
+ internal sealed class InvokeTypeInfo : TraceLoggingTypeInfo
+ {
+ private readonly PropertyAnalysis[] properties;
+
+ public InvokeTypeInfo(
+ Type type,
+ TypeAnalysis typeAnalysis)
+ : base(
+ type,
+ typeAnalysis.name,
+ typeAnalysis.level,
+ typeAnalysis.opcode,
+ typeAnalysis.keywords,
+ typeAnalysis.tags)
+ {
+ if (typeAnalysis.properties.Length != 0)
+ this.properties = typeAnalysis.properties;
+ }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ var groupCollector = collector.AddGroup(name);
+ if (this.properties != null)
+ {
+ foreach (var property in this.properties)
+ {
+ var propertyFormat = EventFieldFormat.Default;
+ var propertyAttribute = property.fieldAttribute;
+ if (propertyAttribute != null)
+ {
+ groupCollector.Tags = propertyAttribute.Tags;
+ propertyFormat = propertyAttribute.Format;
+ }
+
+ property.typeInfo.WriteMetadata(
+ groupCollector,
+ property.name,
+ propertyFormat);
+ }
+ }
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ if (this.properties != null)
+ {
+ foreach (var property in this.properties)
+ {
+ property.typeInfo.WriteData(collector, property.getter(value));
+ }
+ }
+ }
+
+ public override object GetData(object value)
+ {
+ if (this.properties != null)
+ {
+ var membersNames = new List<string>();
+ var memebersValues = new List<object>();
+ for (int i = 0; i < this.properties.Length; i++)
+ {
+ var propertyValue = properties[i].propertyInfo.GetValue(value);
+ membersNames.Add(properties[i].name);
+ memebersValues.Add(properties[i].typeInfo.GetData(propertyValue));
+ }
+ return new EventPayload(membersNames, memebersValues);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs
new file mode 100644
index 0000000000..668043ae68
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/NameInfo.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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 Interlocked = System.Threading.Interlocked;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: Stores the metadata and event identifier corresponding
+ /// to a tracelogging event type+name+tags combination.
+ /// </summary>
+ internal sealed class NameInfo
+ : ConcurrentSetItem<KeyValuePair<string, EventTags>, NameInfo>
+ {
+ /// <summary>
+ /// Insure that eventIds strictly less than 'eventId' will not be
+ /// used by the SelfDescribing events.
+ /// </summary>
+ internal static void ReserveEventIDsBelow(int eventId)
+ {
+ for(;;)
+ {
+ int snapshot = lastIdentity;
+ int newIdentity = (lastIdentity & ~0xFFFFFF) + eventId;
+ newIdentity = Math.Max(newIdentity, snapshot); // Should be redundant. as we only create descriptors once.
+ if (Interlocked.CompareExchange(ref lastIdentity, newIdentity, snapshot) == snapshot)
+ break;
+ }
+ }
+
+ private static int lastIdentity = Statics.TraceLoggingChannel << 24;
+ internal readonly string name;
+ internal readonly EventTags tags;
+ internal readonly int identity;
+ internal readonly byte[] nameMetadata;
+
+ public NameInfo(string name, EventTags tags, int typeMetadataSize)
+ {
+ this.name = name;
+ this.tags = tags & Statics.EventTagsMask;
+ this.identity = Interlocked.Increment(ref lastIdentity);
+
+ int tagsPos = 0;
+ Statics.EncodeTags((int)this.tags, ref tagsPos, null);
+
+ this.nameMetadata = Statics.MetadataForString(name, tagsPos, 0, typeMetadataSize);
+
+ tagsPos = 2;
+ Statics.EncodeTags((int)this.tags, ref tagsPos, this.nameMetadata);
+ }
+
+ public override int Compare(NameInfo other)
+ {
+ return this.Compare(other.name, other.tags);
+ }
+
+ public override int Compare(KeyValuePair<string, EventTags> key)
+ {
+ return this.Compare(key.Key, key.Value & Statics.EventTagsMask);
+ }
+
+ private int Compare(string otherName, EventTags otherTags)
+ {
+ int result = StringComparer.Ordinal.Compare(this.name, otherName);
+ if (result == 0 && this.tags != otherTags)
+ {
+ result = this.tags < otherTags ? -1 : 1;
+ }
+ return result;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs
new file mode 100644
index 0000000000..1f07539b52
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyAnalysis.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Reflection;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: stores the per-property information obtained by
+ /// reflecting over a type.
+ /// </summary>
+ internal sealed class PropertyAnalysis
+ {
+ internal readonly string name;
+ internal readonly PropertyInfo propertyInfo;
+ internal readonly Func<PropertyValue, PropertyValue> getter;
+ internal readonly TraceLoggingTypeInfo typeInfo;
+ internal readonly EventFieldAttribute fieldAttribute;
+
+ public PropertyAnalysis(
+ string name,
+ PropertyInfo propertyInfo,
+ TraceLoggingTypeInfo typeInfo,
+ EventFieldAttribute fieldAttribute)
+ {
+ this.name = name;
+ this.propertyInfo = propertyInfo;
+ this.getter = PropertyValue.GetPropertyGetter(propertyInfo);
+ this.typeInfo = typeInfo;
+ this.fieldAttribute = fieldAttribute;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs
new file mode 100644
index 0000000000..3ea781252f
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs
@@ -0,0 +1,252 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+namespace System.Diagnostics.Tracing
+{
+ /// <summary>
+ /// Holds property values of any type. For common value types, we have inline storage so that we don't need
+ /// to box the values. For all other types, we store the value in a single object reference field.
+ ///
+ /// To get the value of a property quickly, use a delegate produced by <see cref="PropertyValue.GetPropertyGetter(PropertyInfo)"/>.
+ /// </summary>
+ internal unsafe struct PropertyValue
+ {
+ /// <summary>
+ /// Union of well-known value types, to avoid boxing those types.
+ /// </summary>
+ [StructLayout(LayoutKind.Explicit)]
+ public struct Scalar
+ {
+ [FieldOffset(0)]
+ public Boolean AsBoolean;
+ [FieldOffset(0)]
+ public Byte AsByte;
+ [FieldOffset(0)]
+ public SByte AsSByte;
+ [FieldOffset(0)]
+ public Char AsChar;
+ [FieldOffset(0)]
+ public Int16 AsInt16;
+ [FieldOffset(0)]
+ public UInt16 AsUInt16;
+ [FieldOffset(0)]
+ public Int32 AsInt32;
+ [FieldOffset(0)]
+ public UInt32 AsUInt32;
+ [FieldOffset(0)]
+ public Int64 AsInt64;
+ [FieldOffset(0)]
+ public UInt64 AsUInt64;
+ [FieldOffset(0)]
+ public IntPtr AsIntPtr;
+ [FieldOffset(0)]
+ public UIntPtr AsUIntPtr;
+ [FieldOffset(0)]
+ public Single AsSingle;
+ [FieldOffset(0)]
+ public Double AsDouble;
+ [FieldOffset(0)]
+ public Guid AsGuid;
+ [FieldOffset(0)]
+ public DateTime AsDateTime;
+ [FieldOffset(0)]
+ public DateTimeOffset AsDateTimeOffset;
+ [FieldOffset(0)]
+ public TimeSpan AsTimeSpan;
+ [FieldOffset(0)]
+ public Decimal AsDecimal;
+ }
+
+ // Anything not covered by the Scalar union gets stored in this reference.
+ readonly object _reference;
+ readonly Scalar _scalar;
+ readonly int _scalarLength;
+
+ private PropertyValue(object value)
+ {
+ _reference = value;
+ _scalar = default(Scalar);
+ _scalarLength = 0;
+ }
+
+ private PropertyValue(Scalar scalar, int scalarLength)
+ {
+ _reference = null;
+ _scalar = scalar;
+ _scalarLength = scalarLength;
+ }
+
+ private PropertyValue(Boolean value) : this(new Scalar() { AsBoolean = value }, sizeof(Boolean)) { }
+ private PropertyValue(Byte value) : this(new Scalar() { AsByte = value }, sizeof(Byte)) { }
+ private PropertyValue(SByte value) : this(new Scalar() { AsSByte = value }, sizeof(SByte)) { }
+ private PropertyValue(Char value) : this(new Scalar() { AsChar = value }, sizeof(Char)) { }
+ private PropertyValue(Int16 value) : this(new Scalar() { AsInt16 = value }, sizeof(Int16)) { }
+ private PropertyValue(UInt16 value) : this(new Scalar() { AsUInt16 = value }, sizeof(UInt16)) { }
+ private PropertyValue(Int32 value) : this(new Scalar() { AsInt32 = value }, sizeof(Int32)) { }
+ private PropertyValue(UInt32 value) : this(new Scalar() { AsUInt32 = value }, sizeof(UInt32)) { }
+ private PropertyValue(Int64 value) : this(new Scalar() { AsInt64 = value }, sizeof(Int64)) { }
+ private PropertyValue(UInt64 value) : this(new Scalar() { AsUInt64 = value }, sizeof(UInt64)) { }
+ private PropertyValue(IntPtr value) : this(new Scalar() { AsIntPtr = value }, sizeof(IntPtr)) { }
+ private PropertyValue(UIntPtr value) : this(new Scalar() { AsUIntPtr = value }, sizeof(UIntPtr)) { }
+ private PropertyValue(Single value) : this(new Scalar() { AsSingle = value }, sizeof(Single)) { }
+ private PropertyValue(Double value) : this(new Scalar() { AsDouble = value }, sizeof(Double)) { }
+ private PropertyValue(Guid value) : this(new Scalar() { AsGuid = value }, sizeof(Guid)) { }
+ private PropertyValue(DateTime value) : this(new Scalar() { AsDateTime = value }, sizeof(DateTime)) { }
+ private PropertyValue(DateTimeOffset value) : this(new Scalar() { AsDateTimeOffset = value }, sizeof(DateTimeOffset)) { }
+ private PropertyValue(TimeSpan value) : this(new Scalar() { AsTimeSpan = value }, sizeof(TimeSpan)) { }
+ private PropertyValue(Decimal value) : this(new Scalar() { AsDecimal = value }, sizeof(Decimal)) { }
+
+ public static Func<object, PropertyValue> GetFactory(Type type)
+ {
+ if (type == typeof(Boolean)) return value => new PropertyValue((Boolean)value);
+ if (type == typeof(Byte)) return value => new PropertyValue((Byte)value);
+ if (type == typeof(SByte)) return value => new PropertyValue((SByte)value);
+ if (type == typeof(Char)) return value => new PropertyValue((Char)value);
+ if (type == typeof(Int16)) return value => new PropertyValue((Int16)value);
+ if (type == typeof(UInt16)) return value => new PropertyValue((UInt16)value);
+ if (type == typeof(Int32)) return value => new PropertyValue((Int32)value);
+ if (type == typeof(UInt32)) return value => new PropertyValue((UInt32)value);
+ if (type == typeof(Int64)) return value => new PropertyValue((Int64)value);
+ if (type == typeof(UInt64)) return value => new PropertyValue((UInt64)value);
+ if (type == typeof(IntPtr)) return value => new PropertyValue((IntPtr)value);
+ if (type == typeof(UIntPtr)) return value => new PropertyValue((UIntPtr)value);
+ if (type == typeof(Single)) return value => new PropertyValue((Single)value);
+ if (type == typeof(Double)) return value => new PropertyValue((Double)value);
+ if (type == typeof(Guid)) return value => new PropertyValue((Guid)value);
+ if (type == typeof(DateTime)) return value => new PropertyValue((DateTime)value);
+ if (type == typeof(DateTimeOffset)) return value => new PropertyValue((DateTimeOffset)value);
+ if (type == typeof(TimeSpan)) return value => new PropertyValue((TimeSpan)value);
+ if (type == typeof(Decimal)) return value => new PropertyValue((Decimal)value);
+
+ return value => new PropertyValue(value);
+ }
+
+
+ public object ReferenceValue
+ {
+ get
+ {
+ Debug.Assert(_scalarLength == 0, "This ReflectedValue refers to an unboxed value type, not a reference type or boxed value type.");
+ return _reference;
+ }
+ }
+
+ public Scalar ScalarValue
+ {
+ get
+ {
+ Debug.Assert(_scalarLength > 0, "This ReflectedValue refers to a reference type or boxed value type, not an unboxed value type");
+ return _scalar;
+ }
+ }
+
+ public int ScalarLength
+ {
+ get
+ {
+ Debug.Assert(_scalarLength > 0, "This ReflectedValue refers to a reference type or boxed value type, not an unboxed value type");
+ return _scalarLength;
+ }
+ }
+
+ /// <summary>
+ /// Gets a delegate that gets the value of a given property.
+ /// </summary>
+ public static Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property)
+ {
+ if (property.DeclaringType.GetTypeInfo().IsValueType)
+ return GetBoxedValueTypePropertyGetter(property);
+ else
+ return GetReferenceTypePropertyGetter(property);
+ }
+
+ /// <summary>
+ /// Gets a delegate that gets the value of a property of a value type. We unfortunately cannot avoid boxing the value type,
+ /// without making this generic over the value type. That would result in a large number of generic instantiations, and furthermore
+ /// does not work correctly on .Net Native (we cannot express the needed instantiations in an rd.xml file). We expect that user-defined
+ /// value types will be rare, and in any case the boxing only happens for events that are actually enabled.
+ /// </summary>
+ private static Func<PropertyValue, PropertyValue> GetBoxedValueTypePropertyGetter(PropertyInfo property)
+ {
+ var type = property.PropertyType;
+
+ if (type.GetTypeInfo().IsEnum)
+ type = Enum.GetUnderlyingType(type);
+
+ var factory = GetFactory(type);
+
+ return container => factory(property.GetValue(container.ReferenceValue));
+ }
+
+ /// <summary>
+ /// For properties of reference types, we use a generic helper class to get the value. This enables us to use MethodInfo.CreateDelegate
+ /// to build a fast getter. We can get away with this on .Net Native, because we really only need one runtime instantiation of the
+ /// generic type, since it's only instantiated over reference types (and thus all instances are shared).
+ /// </summary>
+ /// <param name="property"></param>
+ /// <returns></returns>
+ private static Func<PropertyValue, PropertyValue> GetReferenceTypePropertyGetter(PropertyInfo property)
+ {
+ var helper = (TypeHelper)Activator.CreateInstance(typeof(ReferenceTypeHelper<>).MakeGenericType(property.DeclaringType));
+ return helper.GetPropertyGetter(property);
+ }
+
+ private abstract class TypeHelper
+ {
+ public abstract Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property);
+
+ protected Delegate GetGetMethod(PropertyInfo property, Type propertyType)
+ {
+ return property.GetMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(property.DeclaringType, propertyType));
+ }
+ }
+
+ private sealed class ReferenceTypeHelper<TContainer> : TypeHelper where TContainer : class
+ {
+ public override Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property)
+ {
+ var type = property.PropertyType;
+
+ if (!Statics.IsValueType(type))
+ {
+ var getter = (Func<TContainer, object>)GetGetMethod(property, type);
+ return container => new PropertyValue(getter((TContainer)container.ReferenceValue));
+ }
+ else
+ {
+ if (type.GetTypeInfo().IsEnum)
+ type = Enum.GetUnderlyingType(type);
+
+ if (type == typeof(Boolean)) { var f = (Func<TContainer, Boolean>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Byte)) { var f = (Func<TContainer, Byte>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(SByte)) { var f = (Func<TContainer, SByte>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Char)) { var f = (Func<TContainer, Char>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Int16)) { var f = (Func<TContainer, Int16>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(UInt16)) { var f = (Func<TContainer, UInt16>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Int32)) { var f = (Func<TContainer, Int32>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(UInt32)) { var f = (Func<TContainer, UInt32>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Int64)) { var f = (Func<TContainer, Int64>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(UInt64)) { var f = (Func<TContainer, UInt64>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(IntPtr)) { var f = (Func<TContainer, IntPtr>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(UIntPtr)) { var f = (Func<TContainer, UIntPtr>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Single)) { var f = (Func<TContainer, Single>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Double)) { var f = (Func<TContainer, Double>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Guid)) { var f = (Func<TContainer, Guid>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(DateTime)) { var f = (Func<TContainer, DateTime>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(DateTimeOffset)) { var f = (Func<TContainer, DateTimeOffset>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(TimeSpan)) { var f = (Func<TContainer, TimeSpan>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+ if (type == typeof(Decimal)) { var f = (Func<TContainer, Decimal>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); }
+
+ return container => new PropertyValue(property.GetValue(container.ReferenceValue));
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs
new file mode 100644
index 0000000000..cdced968f0
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleEventTypes.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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 Interlocked = System.Threading.Interlocked;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: Contains the metadata needed to emit an event, optimized
+ /// for events with one top-level compile-time-typed payload object.
+ /// </summary>
+ /// <typeparam name="T">
+ /// Type of the top-level payload object. Should be EmptyStruct if the
+ /// event has no payload.
+ /// </typeparam>
+ internal static class SimpleEventTypes<T>
+ {
+ private static TraceLoggingEventTypes instance;
+
+ public static TraceLoggingEventTypes Instance
+ {
+ get { return instance ?? InitInstance(); }
+ }
+
+ private static TraceLoggingEventTypes InitInstance()
+ {
+ var info = TraceLoggingTypeInfo.GetInstance(typeof(T), null);
+ var newInstance = new TraceLoggingEventTypes(info.Name, info.Tags, new TraceLoggingTypeInfo[] { info });
+ Interlocked.CompareExchange(ref instance, newInstance, null);
+ return instance;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs
new file mode 100644
index 0000000000..901a0ed1a2
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/SimpleTypeInfos.cs
@@ -0,0 +1,297 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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 System.Reflection;
+using System.Diagnostics;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: Type handler for empty or unsupported types.
+ /// </summary>
+ internal sealed class NullTypeInfo : TraceLoggingTypeInfo
+ {
+ public NullTypeInfo() : base(typeof(EmptyStruct)) { }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ collector.AddGroup(name);
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ return;
+ }
+
+ public override object GetData(object value)
+ {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Type handler for simple scalar types.
+ /// </summary>
+ sealed class ScalarTypeInfo : TraceLoggingTypeInfo
+ {
+ Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc;
+ TraceLoggingDataType nativeFormat;
+
+ private ScalarTypeInfo(
+ Type type,
+ Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc,
+ TraceLoggingDataType nativeFormat)
+ : base(type)
+ {
+ this.formatFunc = formatFunc;
+ this.nativeFormat = nativeFormat;
+ }
+
+ public override void WriteMetadata(TraceLoggingMetadataCollector collector, string name, EventFieldFormat format)
+ {
+ collector.AddScalar(name, formatFunc(format, nativeFormat));
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ collector.AddScalar(value);
+ }
+
+ public static TraceLoggingTypeInfo Boolean() { return new ScalarTypeInfo(typeof(Boolean), Statics.Format8, TraceLoggingDataType.Boolean8); }
+ public static TraceLoggingTypeInfo Byte() { return new ScalarTypeInfo(typeof(Byte), Statics.Format8, TraceLoggingDataType.UInt8); }
+ public static TraceLoggingTypeInfo SByte() { return new ScalarTypeInfo(typeof(SByte), Statics.Format8, TraceLoggingDataType.Int8); }
+ public static TraceLoggingTypeInfo Char() { return new ScalarTypeInfo(typeof(Char), Statics.Format16, TraceLoggingDataType.Char16); }
+ public static TraceLoggingTypeInfo Int16() { return new ScalarTypeInfo(typeof(Int16), Statics.Format16, TraceLoggingDataType.Int16); }
+ public static TraceLoggingTypeInfo UInt16() { return new ScalarTypeInfo(typeof(UInt16), Statics.Format16, TraceLoggingDataType.UInt16); }
+ public static TraceLoggingTypeInfo Int32() { return new ScalarTypeInfo(typeof(Int32), Statics.Format32, TraceLoggingDataType.Int32); }
+ public static TraceLoggingTypeInfo UInt32() { return new ScalarTypeInfo(typeof(UInt32), Statics.Format32, TraceLoggingDataType.UInt32); }
+ public static TraceLoggingTypeInfo Int64() { return new ScalarTypeInfo(typeof(Int64), Statics.Format64, TraceLoggingDataType.Int64); }
+ public static TraceLoggingTypeInfo UInt64() { return new ScalarTypeInfo(typeof(UInt64), Statics.Format64, TraceLoggingDataType.UInt64); }
+ public static TraceLoggingTypeInfo IntPtr() { return new ScalarTypeInfo(typeof(IntPtr), Statics.FormatPtr, Statics.IntPtrType); }
+ public static TraceLoggingTypeInfo UIntPtr() { return new ScalarTypeInfo(typeof(UIntPtr), Statics.FormatPtr, Statics.UIntPtrType); }
+ public static TraceLoggingTypeInfo Single() { return new ScalarTypeInfo(typeof(Single), Statics.Format32, TraceLoggingDataType.Float); }
+ public static TraceLoggingTypeInfo Double() { return new ScalarTypeInfo(typeof(Double), Statics.Format64, TraceLoggingDataType.Double); }
+ public static TraceLoggingTypeInfo Guid() { return new ScalarTypeInfo(typeof(Guid), (f, t) => Statics.MakeDataType(TraceLoggingDataType.Guid, f), TraceLoggingDataType.Guid); }
+ }
+
+
+ /// <summary>
+ /// Type handler for arrays of scalars
+ /// </summary>
+ internal sealed class ScalarArrayTypeInfo : TraceLoggingTypeInfo
+ {
+ Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc;
+ TraceLoggingDataType nativeFormat;
+ int elementSize;
+
+ private ScalarArrayTypeInfo(
+ Type type,
+ Func<EventFieldFormat, TraceLoggingDataType, TraceLoggingDataType> formatFunc,
+ TraceLoggingDataType nativeFormat,
+ int elementSize)
+ : base(type)
+ {
+ this.formatFunc = formatFunc;
+ this.nativeFormat = nativeFormat;
+ this.elementSize = elementSize;
+ }
+
+ public override void WriteMetadata(TraceLoggingMetadataCollector collector, string name, EventFieldFormat format)
+ {
+ collector.AddArray(name, formatFunc(format, nativeFormat));
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ collector.AddArray(value, elementSize);
+ }
+
+ public static TraceLoggingTypeInfo Boolean() { return new ScalarArrayTypeInfo(typeof(Boolean[]), Statics.Format8, TraceLoggingDataType.Boolean8, sizeof(Boolean)); }
+ public static TraceLoggingTypeInfo Byte() { return new ScalarArrayTypeInfo(typeof(Byte[]), Statics.Format8, TraceLoggingDataType.UInt8, sizeof(Byte)); }
+ public static TraceLoggingTypeInfo SByte() { return new ScalarArrayTypeInfo(typeof(SByte[]), Statics.Format8, TraceLoggingDataType.Int8, sizeof(SByte)); }
+ public static TraceLoggingTypeInfo Char() { return new ScalarArrayTypeInfo(typeof(Char[]), Statics.Format16, TraceLoggingDataType.Char16, sizeof(Char)); }
+ public static TraceLoggingTypeInfo Int16() { return new ScalarArrayTypeInfo(typeof(Int16[]), Statics.Format16, TraceLoggingDataType.Int16, sizeof(Int16)); }
+ public static TraceLoggingTypeInfo UInt16() { return new ScalarArrayTypeInfo(typeof(UInt16[]), Statics.Format16, TraceLoggingDataType.UInt16, sizeof(UInt16)); }
+ public static TraceLoggingTypeInfo Int32() { return new ScalarArrayTypeInfo(typeof(Int32[]), Statics.Format32, TraceLoggingDataType.Int32, sizeof(Int32)); }
+ public static TraceLoggingTypeInfo UInt32() { return new ScalarArrayTypeInfo(typeof(UInt32[]), Statics.Format32, TraceLoggingDataType.UInt32, sizeof(UInt32)); }
+ public static TraceLoggingTypeInfo Int64() { return new ScalarArrayTypeInfo(typeof(Int64[]), Statics.Format64, TraceLoggingDataType.Int64, sizeof(Int64)); }
+ public static TraceLoggingTypeInfo UInt64() { return new ScalarArrayTypeInfo(typeof(UInt64[]), Statics.Format64, TraceLoggingDataType.UInt64, sizeof(UInt64)); }
+ public static TraceLoggingTypeInfo IntPtr() { return new ScalarArrayTypeInfo(typeof(IntPtr[]), Statics.FormatPtr, Statics.IntPtrType, System.IntPtr.Size); }
+ public static TraceLoggingTypeInfo UIntPtr() { return new ScalarArrayTypeInfo(typeof(UIntPtr[]), Statics.FormatPtr, Statics.UIntPtrType, System.IntPtr.Size); }
+ public static TraceLoggingTypeInfo Single() { return new ScalarArrayTypeInfo(typeof(Single[]), Statics.Format32, TraceLoggingDataType.Float, sizeof(Single)); }
+ public static TraceLoggingTypeInfo Double() { return new ScalarArrayTypeInfo(typeof(Double[]), Statics.Format64, TraceLoggingDataType.Double, sizeof(Double)); }
+ public unsafe static TraceLoggingTypeInfo Guid() { return new ScalarArrayTypeInfo(typeof(Guid), (f, t) => Statics.MakeDataType(TraceLoggingDataType.Guid, f), TraceLoggingDataType.Guid, sizeof(Guid)); }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for String.
+ /// </summary>
+ internal sealed class StringTypeInfo : TraceLoggingTypeInfo
+ {
+ public StringTypeInfo() : base(typeof(string)) { }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ collector.AddBinary(name, Statics.MakeDataType(TraceLoggingDataType.CountedUtf16String, format));
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ collector.AddBinary((string)value.ReferenceValue);
+ }
+
+ public override object GetData(object value)
+ {
+ if(value == null)
+ {
+ return "";
+ }
+
+ return value;
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for DateTime.
+ /// </summary>
+ internal sealed class DateTimeTypeInfo : TraceLoggingTypeInfo
+ {
+ public DateTimeTypeInfo() : base(typeof(DateTime)) { }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ collector.AddScalar(name, Statics.MakeDataType(TraceLoggingDataType.FileTime, format));
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ var ticks = value.ScalarValue.AsDateTime.Ticks;
+ collector.AddScalar(ticks < 504911232000000000 ? 0 : ticks - 504911232000000000);
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for DateTimeOffset.
+ /// </summary>
+ internal sealed class DateTimeOffsetTypeInfo : TraceLoggingTypeInfo
+ {
+ public DateTimeOffsetTypeInfo() : base(typeof(DateTimeOffset)) { }
+
+ public override void WriteMetadata(TraceLoggingMetadataCollector collector, string name, EventFieldFormat format)
+ {
+ var group = collector.AddGroup(name);
+ group.AddScalar("Ticks", Statics.MakeDataType(TraceLoggingDataType.FileTime, format));
+ group.AddScalar("Offset", TraceLoggingDataType.Int64);
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ var dateTimeOffset = value.ScalarValue.AsDateTimeOffset;
+ var ticks = dateTimeOffset.Ticks;
+ collector.AddScalar(ticks < 504911232000000000 ? 0 : ticks - 504911232000000000);
+ collector.AddScalar(dateTimeOffset.Offset.Ticks);
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for TimeSpan.
+ /// </summary>
+ internal sealed class TimeSpanTypeInfo : TraceLoggingTypeInfo
+ {
+ public TimeSpanTypeInfo() : base(typeof(TimeSpan)) { }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ collector.AddScalar(name, Statics.MakeDataType(TraceLoggingDataType.Int64, format));
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ collector.AddScalar(value.ScalarValue.AsTimeSpan.Ticks);
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for Decimal. (Note: not full-fidelity, exposed as Double.)
+ /// </summary>
+ internal sealed class DecimalTypeInfo : TraceLoggingTypeInfo
+ {
+ public DecimalTypeInfo() : base(typeof(Decimal)) { }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ collector.AddScalar(name, Statics.MakeDataType(TraceLoggingDataType.Double, format));
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ collector.AddScalar((double)value.ScalarValue.AsDecimal);
+ }
+ }
+
+ /// <summary>
+ /// TraceLogging: Type handler for Nullable.
+ /// </summary>
+ internal sealed class NullableTypeInfo : TraceLoggingTypeInfo
+ {
+ private readonly TraceLoggingTypeInfo valueInfo;
+ private readonly Func<PropertyValue, PropertyValue> hasValueGetter;
+ private readonly Func<PropertyValue, PropertyValue> valueGetter;
+
+ public NullableTypeInfo(Type type, List<Type> recursionCheck)
+ : base(type)
+ {
+ var typeArgs = type.GenericTypeArguments;
+ Debug.Assert(typeArgs.Length == 1);
+ this.valueInfo = TraceLoggingTypeInfo.GetInstance(typeArgs[0], recursionCheck);
+ this.hasValueGetter = PropertyValue.GetPropertyGetter(type.GetTypeInfo().GetDeclaredProperty("HasValue"));
+ this.valueGetter = PropertyValue.GetPropertyGetter(type.GetTypeInfo().GetDeclaredProperty("Value"));
+ }
+
+ public override void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format)
+ {
+ var group = collector.AddGroup(name);
+ group.AddScalar("HasValue", TraceLoggingDataType.Boolean8);
+ this.valueInfo.WriteMetadata(group, "Value", format);
+ }
+
+ public override void WriteData(TraceLoggingDataCollector collector, PropertyValue value)
+ {
+ var hasValue = hasValueGetter(value);
+ collector.AddScalar(hasValue);
+ var val = hasValue.ScalarValue.AsBoolean ? valueGetter(value) : valueInfo.PropertyValueFactory(Activator.CreateInstance(valueInfo.DataType));
+ this.valueInfo.WriteData(collector, val);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs
new file mode 100644
index 0000000000..9fa776753d
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/Statics.cs
@@ -0,0 +1,727 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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 System.Reflection;
+using System.Runtime.CompilerServices;
+using Encoding = System.Text.Encoding;
+
+using Microsoft.Reflection;
+
+#if ES_BUILD_STANDALONE
+using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: Constants and utility functions.
+ /// </summary>
+ internal static class Statics
+ {
+ #region Constants
+
+ public const byte DefaultLevel = 5;
+ public const byte TraceLoggingChannel = 0xb;
+ public const byte InTypeMask = 31;
+ public const byte InTypeFixedCountFlag = 32;
+ public const byte InTypeVariableCountFlag = 64;
+ public const byte InTypeCustomCountFlag = 96;
+ public const byte InTypeCountMask = 96;
+ public const byte InTypeChainFlag = 128;
+ public const byte OutTypeMask = 127;
+ public const byte OutTypeChainFlag = 128;
+ public const EventTags EventTagsMask = (EventTags)0xfffffff;
+
+ public static readonly TraceLoggingDataType IntPtrType = IntPtr.Size == 8
+ ? TraceLoggingDataType.Int64
+ : TraceLoggingDataType.Int32;
+ public static readonly TraceLoggingDataType UIntPtrType = IntPtr.Size == 8
+ ? TraceLoggingDataType.UInt64
+ : TraceLoggingDataType.UInt32;
+ public static readonly TraceLoggingDataType HexIntPtrType = IntPtr.Size == 8
+ ? TraceLoggingDataType.HexInt64
+ : TraceLoggingDataType.HexInt32;
+
+ #endregion
+
+ #region Metadata helpers
+
+ /// <summary>
+ /// A complete metadata chunk can be expressed as:
+ /// length16 + prefix + null-terminated-utf8-name + suffix + additionalData.
+ /// We assume that excludedData will be provided by some other means,
+ /// but that its size is known. This function returns a blob containing
+ /// length16 + prefix + name + suffix, with prefix and suffix initialized
+ /// to 0's. The length16 value is initialized to the length of the returned
+ /// blob plus additionalSize, so that the concatenation of the returned blob
+ /// plus a blob of size additionalSize constitutes a valid metadata blob.
+ /// </summary>
+ /// <param name="name">
+ /// The name to include in the blob.
+ /// </param>
+ /// <param name="prefixSize">
+ /// Amount of space to reserve before name. For provider or field blobs, this
+ /// should be 0. For event blobs, this is used for the tags field and will vary
+ /// from 1 to 4, depending on how large the tags field needs to be.
+ /// </param>
+ /// <param name="suffixSize">
+ /// Amount of space to reserve after name. For example, a provider blob with no
+ /// traits would reserve 0 extra bytes, but a provider blob with a single GroupId
+ /// field would reserve 19 extra bytes.
+ /// </param>
+ /// <param name="additionalSize">
+ /// Amount of additional data in another blob. This value will be counted in the
+ /// blob's length field, but will not be included in the returned byte[] object.
+ /// The complete blob would then be the concatenation of the returned byte[] object
+ /// with another byte[] object of length additionalSize.
+ /// </param>
+ /// <returns>
+ /// A byte[] object with the length and name fields set, with room reserved for
+ /// prefix and suffix. If additionalSize was 0, the byte[] object is a complete
+ /// blob. Otherwise, another byte[] of size additionalSize must be concatenated
+ /// with this one to form a complete blob.
+ /// </returns>
+ public static byte[] MetadataForString(
+ string name,
+ int prefixSize,
+ int suffixSize,
+ int additionalSize)
+ {
+ Statics.CheckName(name);
+ int metadataSize = Encoding.UTF8.GetByteCount(name) + 3 + prefixSize + suffixSize;
+ var metadata = new byte[metadataSize];
+ ushort totalSize = checked((ushort)(metadataSize + additionalSize));
+ metadata[0] = unchecked((byte)totalSize);
+ metadata[1] = unchecked((byte)(totalSize >> 8));
+ Encoding.UTF8.GetBytes(name, 0, name.Length, metadata, 2 + prefixSize);
+ return metadata;
+ }
+
+ /// <summary>
+ /// Serialize the low 28 bits of the tags value into the metadata stream,
+ /// starting at the index given by pos. Updates pos. Writes 1 to 4 bytes,
+ /// depending on the value of the tags variable. Usable for event tags and
+ /// field tags.
+ ///
+ /// 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 static void EncodeTags(int tags, ref int pos, byte[] metadata)
+ {
+ // We transmit the low 28 bits of tags, high bits first, 7 bits at a time.
+ var tagsLeft = tags & 0xfffffff;
+ bool more;
+ do
+ {
+ byte current = (byte)((tagsLeft >> 21) & 0x7f);
+ more = (tagsLeft & 0x1fffff) != 0;
+ current |= (byte)(more ? 0x80 : 0x00);
+ tagsLeft = tagsLeft << 7;
+
+ if (metadata != null)
+ {
+ metadata[pos] = current;
+ }
+ pos += 1;
+ }
+ while (more);
+ }
+
+ public static byte Combine(
+ int settingValue,
+ byte defaultValue)
+ {
+ unchecked
+ {
+ return (byte)settingValue == settingValue
+ ? (byte)settingValue
+ : defaultValue;
+ }
+ }
+
+ public static byte Combine(
+ int settingValue1,
+ int settingValue2,
+ byte defaultValue)
+ {
+ unchecked
+ {
+ return (byte)settingValue1 == settingValue1
+ ? (byte)settingValue1
+ : (byte)settingValue2 == settingValue2
+ ? (byte)settingValue2
+ : defaultValue;
+ }
+ }
+
+ public static int Combine(
+ int settingValue1,
+ int settingValue2)
+ {
+ unchecked
+ {
+ return (byte)settingValue1 == settingValue1
+ ? settingValue1
+ : settingValue2;
+ }
+ }
+
+ public static void CheckName(string name)
+ {
+ if (name != null && 0 <= name.IndexOf('\0'))
+ {
+ throw new ArgumentOutOfRangeException(nameof(name));
+ }
+ }
+
+ public static bool ShouldOverrideFieldName(string fieldName)
+ {
+ return (fieldName.Length <= 2 && fieldName[0] == '_');
+ }
+
+ public static TraceLoggingDataType MakeDataType(
+ TraceLoggingDataType baseType,
+ EventFieldFormat format)
+ {
+ return (TraceLoggingDataType)(((int)baseType & 0x1f) | ((int)format << 8));
+ }
+
+ /// <summary>
+ /// Adjusts the native type based on format.
+ /// - If format is default, return native.
+ /// - If format is recognized, return the canonical type for that format.
+ /// - Otherwise remove existing format from native and apply the requested format.
+ /// </summary>
+ public static TraceLoggingDataType Format8(
+ EventFieldFormat format,
+ TraceLoggingDataType native)
+ {
+ switch (format)
+ {
+ case EventFieldFormat.Default:
+ return native;
+ case EventFieldFormat.String:
+ return TraceLoggingDataType.Char8;
+ case EventFieldFormat.Boolean:
+ return TraceLoggingDataType.Boolean8;
+ case EventFieldFormat.Hexadecimal:
+ return TraceLoggingDataType.HexInt8;
+#if false
+ case EventSourceFieldFormat.Signed:
+ return TraceLoggingDataType.Int8;
+ case EventSourceFieldFormat.Unsigned:
+ return TraceLoggingDataType.UInt8;
+#endif
+ default:
+ return MakeDataType(native, format);
+ }
+ }
+
+ /// <summary>
+ /// Adjusts the native type based on format.
+ /// - If format is default, return native.
+ /// - If format is recognized, return the canonical type for that format.
+ /// - Otherwise remove existing format from native and apply the requested format.
+ /// </summary>
+ public static TraceLoggingDataType Format16(
+ EventFieldFormat format,
+ TraceLoggingDataType native)
+ {
+ switch (format)
+ {
+ case EventFieldFormat.Default:
+ return native;
+ case EventFieldFormat.String:
+ return TraceLoggingDataType.Char16;
+ case EventFieldFormat.Hexadecimal:
+ return TraceLoggingDataType.HexInt16;
+#if false
+ case EventSourceFieldFormat.Port:
+ return TraceLoggingDataType.Port;
+ case EventSourceFieldFormat.Signed:
+ return TraceLoggingDataType.Int16;
+ case EventSourceFieldFormat.Unsigned:
+ return TraceLoggingDataType.UInt16;
+#endif
+ default:
+ return MakeDataType(native, format);
+ }
+ }
+
+ /// <summary>
+ /// Adjusts the native type based on format.
+ /// - If format is default, return native.
+ /// - If format is recognized, return the canonical type for that format.
+ /// - Otherwise remove existing format from native and apply the requested format.
+ /// </summary>
+ public static TraceLoggingDataType Format32(
+ EventFieldFormat format,
+ TraceLoggingDataType native)
+ {
+ switch (format)
+ {
+ case EventFieldFormat.Default:
+ return native;
+ case EventFieldFormat.Boolean:
+ return TraceLoggingDataType.Boolean32;
+ case EventFieldFormat.Hexadecimal:
+ return TraceLoggingDataType.HexInt32;
+#if false
+ case EventSourceFieldFormat.Ipv4Address:
+ return TraceLoggingDataType.Ipv4Address;
+ case EventSourceFieldFormat.ProcessId:
+ return TraceLoggingDataType.ProcessId;
+ case EventSourceFieldFormat.ThreadId:
+ return TraceLoggingDataType.ThreadId;
+ case EventSourceFieldFormat.Win32Error:
+ return TraceLoggingDataType.Win32Error;
+ case EventSourceFieldFormat.NTStatus:
+ return TraceLoggingDataType.NTStatus;
+#endif
+ case EventFieldFormat.HResult:
+ return TraceLoggingDataType.HResult;
+#if false
+ case EventSourceFieldFormat.Signed:
+ return TraceLoggingDataType.Int32;
+ case EventSourceFieldFormat.Unsigned:
+ return TraceLoggingDataType.UInt32;
+#endif
+ default:
+ return MakeDataType(native, format);
+ }
+ }
+
+ /// <summary>
+ /// Adjusts the native type based on format.
+ /// - If format is default, return native.
+ /// - If format is recognized, return the canonical type for that format.
+ /// - Otherwise remove existing format from native and apply the requested format.
+ /// </summary>
+ public static TraceLoggingDataType Format64(
+ EventFieldFormat format,
+ TraceLoggingDataType native)
+ {
+ switch (format)
+ {
+ case EventFieldFormat.Default:
+ return native;
+ case EventFieldFormat.Hexadecimal:
+ return TraceLoggingDataType.HexInt64;
+#if false
+ case EventSourceFieldFormat.FileTime:
+ return TraceLoggingDataType.FileTime;
+ case EventSourceFieldFormat.Signed:
+ return TraceLoggingDataType.Int64;
+ case EventSourceFieldFormat.Unsigned:
+ return TraceLoggingDataType.UInt64;
+#endif
+ default:
+ return MakeDataType(native, format);
+ }
+ }
+
+ /// <summary>
+ /// Adjusts the native type based on format.
+ /// - If format is default, return native.
+ /// - If format is recognized, return the canonical type for that format.
+ /// - Otherwise remove existing format from native and apply the requested format.
+ /// </summary>
+ public static TraceLoggingDataType FormatPtr(
+ EventFieldFormat format,
+ TraceLoggingDataType native)
+ {
+ switch (format)
+ {
+ case EventFieldFormat.Default:
+ return native;
+ case EventFieldFormat.Hexadecimal:
+ return HexIntPtrType;
+#if false
+ case EventSourceFieldFormat.Signed:
+ return IntPtrType;
+ case EventSourceFieldFormat.Unsigned:
+ return UIntPtrType;
+#endif
+ default:
+ return MakeDataType(native, format);
+ }
+ }
+
+ #endregion
+
+ #region Reflection helpers
+
+ /*
+ All TraceLogging use of reflection APIs should go through wrappers here.
+ This helps with portability, and it also makes it easier to audit what
+ kinds of reflection operations are being done.
+ */
+
+ public static object CreateInstance(Type type, params object[] parameters)
+ {
+ return Activator.CreateInstance(type, parameters);
+ }
+
+ public static bool IsValueType(Type type)
+ {
+ bool result = type.IsValueType();
+ return result;
+ }
+
+ public static bool IsEnum(Type type)
+ {
+ bool result = type.IsEnum();
+ return result;
+ }
+
+ public static IEnumerable<PropertyInfo> GetProperties(Type type)
+ {
+ IEnumerable<PropertyInfo> result = type.GetProperties();
+ return result;
+ }
+
+ public static MethodInfo GetGetMethod(PropertyInfo propInfo)
+ {
+ MethodInfo result = propInfo.GetGetMethod();
+ return result;
+ }
+
+ public static MethodInfo GetDeclaredStaticMethod(Type declaringType, string name)
+ {
+ MethodInfo result;
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ result = declaringType.GetTypeInfo().GetDeclaredMethod(name);
+#else
+ result = declaringType.GetMethod(
+ name,
+ BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic);
+#endif
+ return result;
+ }
+
+ public static bool HasCustomAttribute(
+ PropertyInfo propInfo,
+ Type attributeType)
+ {
+ bool result;
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ result = propInfo.IsDefined(attributeType);
+#else
+ var attributes = propInfo.GetCustomAttributes(
+ attributeType,
+ false);
+ result = attributes.Length != 0;
+#endif
+ return result;
+ }
+
+ public static AttributeType GetCustomAttribute<AttributeType>(PropertyInfo propInfo)
+ where AttributeType : Attribute
+ {
+ AttributeType result = null;
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ foreach (var attrib in propInfo.GetCustomAttributes<AttributeType>(false))
+ {
+ result = attrib;
+ break;
+ }
+#else
+ var attributes = propInfo.GetCustomAttributes(typeof(AttributeType), false);
+ if (attributes.Length != 0)
+ {
+ result = (AttributeType)attributes[0];
+ }
+#endif
+ return result;
+ }
+
+ public static AttributeType GetCustomAttribute<AttributeType>(Type type)
+ where AttributeType : Attribute
+ {
+ AttributeType result = null;
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ foreach (var attrib in type.GetTypeInfo().GetCustomAttributes<AttributeType>(false))
+ {
+ result = attrib;
+ break;
+ }
+#else
+ var attributes = type.GetCustomAttributes(typeof(AttributeType), false);
+ if (attributes.Length != 0)
+ {
+ result = (AttributeType)attributes[0];
+ }
+#endif
+ return result;
+ }
+
+ public static Type[] GetGenericArguments(Type type)
+ {
+ return type.GetGenericArguments();
+ }
+
+ public static Type FindEnumerableElementType(Type type)
+ {
+ Type elementType = null;
+
+ if (IsGenericMatch(type, typeof(IEnumerable<>)))
+ {
+ elementType = GetGenericArguments(type)[0];
+ }
+ else
+ {
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ var ifaceTypes = type.GetTypeInfo().ImplementedInterfaces;
+#else
+ var ifaceTypes = type.FindInterfaces(IsGenericMatch, typeof(IEnumerable<>));
+#endif
+
+ foreach (var ifaceType in ifaceTypes)
+ {
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ if (!IsGenericMatch(ifaceType, typeof(IEnumerable<>)))
+ {
+ continue;
+ }
+#endif
+
+ if (elementType != null)
+ {
+ // ambiguous match. report no match at all.
+ elementType = null;
+ break;
+ }
+
+ elementType = GetGenericArguments(ifaceType)[0];
+ }
+ }
+
+ return elementType;
+ }
+
+ public static bool IsGenericMatch(Type type, object openType)
+ {
+ return type.IsGenericType() && type.GetGenericTypeDefinition() == (Type)openType;
+ }
+
+ public static Delegate CreateDelegate(Type delegateType, MethodInfo methodInfo)
+ {
+ Delegate result;
+#if (ES_BUILD_PCL || ES_BUILD_PN)
+ result = methodInfo.CreateDelegate(
+ delegateType);
+#else
+ result = Delegate.CreateDelegate(
+ delegateType,
+ methodInfo);
+#endif
+ return result;
+ }
+
+ public static TraceLoggingTypeInfo CreateDefaultTypeInfo(
+ Type dataType,
+ List<Type> recursionCheck)
+ {
+ TraceLoggingTypeInfo result;
+
+ if (recursionCheck.Contains(dataType))
+ {
+ throw new NotSupportedException(Resources.GetResourceString("EventSource_RecursiveTypeDefinition"));
+ }
+
+ recursionCheck.Add(dataType);
+
+ var eventAttrib = Statics.GetCustomAttribute<EventDataAttribute>(dataType);
+ if (eventAttrib != null ||
+ Statics.GetCustomAttribute<CompilerGeneratedAttribute>(dataType) != null ||
+ IsGenericMatch(dataType, typeof(KeyValuePair<,>)))
+ {
+ var analysis = new TypeAnalysis(dataType, eventAttrib, recursionCheck);
+ result = new InvokeTypeInfo(dataType, analysis);
+ }
+ else if (dataType.IsArray)
+ {
+ var elementType = dataType.GetElementType();
+ if (elementType == typeof(Boolean))
+ {
+ result = ScalarArrayTypeInfo.Boolean();
+ }
+ else if (elementType == typeof(Byte))
+ {
+ result = ScalarArrayTypeInfo.Byte();
+ }
+ else if (elementType == typeof(SByte))
+ {
+ result = ScalarArrayTypeInfo.SByte();
+ }
+ else if (elementType == typeof(Int16))
+ {
+ result = ScalarArrayTypeInfo.Int16();
+ }
+ else if (elementType == typeof(UInt16))
+ {
+ result = ScalarArrayTypeInfo.UInt16();
+ }
+ else if (elementType == typeof(Int32))
+ {
+ result = ScalarArrayTypeInfo.Int32();
+ }
+ else if (elementType == typeof(UInt32))
+ {
+ result = ScalarArrayTypeInfo.UInt32();
+ }
+ else if (elementType == typeof(Int64))
+ {
+ result = ScalarArrayTypeInfo.Int64();
+ }
+ else if (elementType == typeof(UInt64))
+ {
+ result = ScalarArrayTypeInfo.UInt64();
+ }
+ else if (elementType == typeof(Char))
+ {
+ result = ScalarArrayTypeInfo.Char();
+ }
+ else if (elementType == typeof(Double))
+ {
+ result = ScalarArrayTypeInfo.Double();
+ }
+ else if (elementType == typeof(Single))
+ {
+ result = ScalarArrayTypeInfo.Single();
+ }
+ else if (elementType == typeof(IntPtr))
+ {
+ result = ScalarArrayTypeInfo.IntPtr();
+ }
+ else if (elementType == typeof(UIntPtr))
+ {
+ result = ScalarArrayTypeInfo.UIntPtr();
+ }
+ else if (elementType == typeof(Guid))
+ {
+ result = ScalarArrayTypeInfo.Guid();
+ }
+ else
+ {
+ result = new ArrayTypeInfo(dataType, TraceLoggingTypeInfo.GetInstance(elementType, recursionCheck));
+ }
+ }
+ else
+ {
+ if (Statics.IsEnum(dataType))
+ dataType = Enum.GetUnderlyingType(dataType);
+
+ if (dataType == typeof(String))
+ {
+ result = new StringTypeInfo();
+ }
+ else if (dataType == typeof(Boolean))
+ {
+ result = ScalarTypeInfo.Boolean();
+ }
+ else if (dataType == typeof(Byte))
+ {
+ result = ScalarTypeInfo.Byte();
+ }
+ else if (dataType == typeof(SByte))
+ {
+ result = ScalarTypeInfo.SByte();
+ }
+ else if (dataType == typeof(Int16))
+ {
+ result = ScalarTypeInfo.Int16();
+ }
+ else if (dataType == typeof(UInt16))
+ {
+ result = ScalarTypeInfo.UInt16();
+ }
+ else if (dataType == typeof(Int32))
+ {
+ result = ScalarTypeInfo.Int32();
+ }
+ else if (dataType == typeof(UInt32))
+ {
+ result = ScalarTypeInfo.UInt32();
+ }
+ else if (dataType == typeof(Int64))
+ {
+ result = ScalarTypeInfo.Int64();
+ }
+ else if (dataType == typeof(UInt64))
+ {
+ result = ScalarTypeInfo.UInt64();
+ }
+ else if (dataType == typeof(Char))
+ {
+ result = ScalarTypeInfo.Char();
+ }
+ else if (dataType == typeof(Double))
+ {
+ result = ScalarTypeInfo.Double();
+ }
+ else if (dataType == typeof(Single))
+ {
+ result = ScalarTypeInfo.Single();
+ }
+ else if (dataType == typeof(DateTime))
+ {
+ result = new DateTimeTypeInfo();
+ }
+ else if (dataType == typeof(Decimal))
+ {
+ result = new DecimalTypeInfo();
+ }
+ else if (dataType == typeof(IntPtr))
+ {
+ result = ScalarTypeInfo.IntPtr();
+ }
+ else if (dataType == typeof(UIntPtr))
+ {
+ result = ScalarTypeInfo.UIntPtr();
+ }
+ else if (dataType == typeof(Guid))
+ {
+ result = ScalarTypeInfo.Guid();
+ }
+ else if (dataType == typeof(TimeSpan))
+ {
+ result = new TimeSpanTypeInfo();
+ }
+ else if (dataType == typeof(DateTimeOffset))
+ {
+ result = new DateTimeOffsetTypeInfo();
+ }
+ else if (dataType == typeof(EmptyStruct))
+ {
+ result = new NullTypeInfo();
+ }
+ else if (IsGenericMatch(dataType, typeof(Nullable<>)))
+ {
+ result = new NullableTypeInfo(dataType, recursionCheck);
+ }
+ else
+ {
+ var elementType = FindEnumerableElementType(dataType);
+ if (elementType != null)
+ {
+ result = new EnumerableTypeInfo(dataType, TraceLoggingTypeInfo.GetInstance(elementType, recursionCheck));
+ }
+ else
+ {
+ throw new ArgumentException(Resources.GetResourceString("EventSource_NonCompliantTypeError", dataType.Name));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs
new file mode 100644
index 0000000000..04a047fb35
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataCollector.cs
@@ -0,0 +1,104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Security;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: Used when implementing a custom TraceLoggingTypeInfo.
+ /// The instance of this type is provided to the TypeInfo.WriteData method.
+ /// All operations are forwarded to the current thread's DataCollector.
+ /// Note that this abstraction would allow us to expose the custom
+ /// serialization system to partially-trusted code. If we end up not
+ /// making custom serialization public, or if we only expose it to
+ /// full-trust code, this abstraction is unnecessary (though it probably
+ /// doesn't hurt anything).
+ /// </summary>
+ internal unsafe class TraceLoggingDataCollector
+ {
+ internal static readonly TraceLoggingDataCollector Instance = new TraceLoggingDataCollector();
+
+ private TraceLoggingDataCollector()
+ {
+ return;
+ }
+
+ /// <summary>
+ /// Marks the start of a non-blittable array or enumerable.
+ /// </summary>
+ /// <returns>Bookmark to be passed to EndBufferedArray.</returns>
+ public int BeginBufferedArray()
+ {
+ return DataCollector.ThreadInstance.BeginBufferedArray();
+ }
+
+ /// <summary>
+ /// Marks the end of a non-blittable array or enumerable.
+ /// </summary>
+ /// <param name="bookmark">The value returned by BeginBufferedArray.</param>
+ /// <param name="count">The number of items in the array.</param>
+ public void EndBufferedArray(int bookmark, int count)
+ {
+ DataCollector.ThreadInstance.EndBufferedArray(bookmark, count);
+ }
+
+ /// <summary>
+ /// Adds the start of a group to the event.
+ /// This has no effect on the event payload, but is provided to allow
+ /// WriteMetadata and WriteData implementations to have similar
+ /// sequences of calls, allowing for easier verification of correctness.
+ /// </summary>
+ public TraceLoggingDataCollector AddGroup()
+ {
+ return this;
+ }
+
+ public void AddScalar(PropertyValue value)
+ {
+ var scalar = value.ScalarValue;
+ DataCollector.ThreadInstance.AddScalar(&scalar, value.ScalarLength);
+ }
+
+ /// <summary>
+ /// Adds an Int64 value to the event payload.
+ /// </summary>
+ /// <param name="value">Value to be added.</param>
+ public void AddScalar(long value)
+ {
+ DataCollector.ThreadInstance.AddScalar(&value, sizeof(long));
+ }
+
+ /// <summary>
+ /// Adds a Double value to the event payload.
+ /// </summary>
+ /// <param name="value">Value to be added.</param>
+ public void AddScalar(double value)
+ {
+ DataCollector.ThreadInstance.AddScalar(&value, sizeof(double));
+ }
+
+ /// <summary>
+ /// Adds a counted String value to the event payload.
+ /// </summary>
+ /// <param name="value">
+ /// Value to be added. A null value is treated as a zero-length string.
+ /// </param>
+ public void AddBinary(string value)
+ {
+ DataCollector.ThreadInstance.AddBinary(value, value == null ? 0 : value.Length * 2);
+ }
+
+ public void AddArray(PropertyValue value, int elementSize)
+ {
+ Array array = (Array)value.ReferenceValue;
+ DataCollector.ThreadInstance.AddArray(array, array == null ? 0 : array.Length, elementSize);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs
new file mode 100644
index 0000000000..529948daf8
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingDataType.cs
@@ -0,0 +1,349 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: Used when implementing a custom TraceLoggingTypeInfo.
+ /// These are passed to metadataCollector.Add to specify the low-level
+ /// type of a field in the event payload. Note that a "formatted"
+ /// TraceLoggingDataType consists of a core TraceLoggingDataType value
+ /// (a TraceLoggingDataType with a value less than 32) plus an OutType.
+ /// Any combination of TraceLoggingDataType + OutType is valid, but not
+ /// all are useful. In particular, combinations not explicitly listed
+ /// below are unlikely to be recognized by decoders, and will typically
+ /// be decoded as the corresponding core type (i.e. the decoder will
+ /// mask off any unrecognized OutType value).
+ /// </summary>
+ internal enum TraceLoggingDataType
+ {
+ /// <summary>
+ /// Core type.
+ /// Data type with no value (0-length payload).
+ /// NOTE: arrays of Nil are illegal.
+ /// NOTE: a fixed-length array of Nil is interpreted by the decoder as
+ /// a struct (obsolete but retained for backwards-compatibility).
+ /// </summary>
+ Nil = 0,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes null-terminated Char16 string.
+ /// Decoding treats as UTF-16LE string.
+ /// </summary>
+ Utf16String = 1,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes null-terminated Char8 string.
+ /// Decoding treats as MBCS string.
+ /// </summary>
+ MbcsString = 2,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 8-bit value.
+ /// Decoding treats as signed integer.
+ /// </summary>
+ Int8 = 3,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 8-bit value.
+ /// Decoding treats as unsigned integer.
+ /// </summary>
+ UInt8 = 4,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 16-bit value.
+ /// Decoding treats as signed integer.
+ /// </summary>
+ Int16 = 5,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 16-bit value.
+ /// Decoding treats as unsigned integer.
+ /// </summary>
+ UInt16 = 6,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as signed integer.
+ /// </summary>
+ Int32 = 7,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as unsigned integer.
+ /// </summary>
+ UInt32 = 8,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 64-bit value.
+ /// Decoding treats as signed integer.
+ /// </summary>
+ Int64 = 9,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 64-bit value.
+ /// Decoding treats as unsigned integer.
+ /// </summary>
+ UInt64 = 10,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as Float.
+ /// </summary>
+ Float = 11,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 64-bit value.
+ /// Decoding treats as Double.
+ /// </summary>
+ Double = 12,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as Boolean.
+ /// </summary>
+ Boolean32 = 13,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 16-bit bytecount followed by binary data.
+ /// Decoding treats as binary data.
+ /// </summary>
+ Binary = 14,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 16-byte value.
+ /// Decoding treats as GUID.
+ /// </summary>
+ Guid = 15,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 64-bit value.
+ /// Decoding treats as FILETIME.
+ /// </summary>
+ FileTime = 17,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 16-byte value.
+ /// Decoding treats as SYSTEMTIME.
+ /// </summary>
+ SystemTime = 18,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as hexadecimal unsigned integer.
+ /// </summary>
+ HexInt32 = 20,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 64-bit value.
+ /// Decoding treats as hexadecimal unsigned integer.
+ /// </summary>
+ HexInt64 = 21,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 16-bit bytecount followed by Char16 data.
+ /// Decoding treats as UTF-16LE string.
+ /// </summary>
+ CountedUtf16String = 22,
+
+ /// <summary>
+ /// Core type.
+ /// Encoding assumes 16-bit bytecount followed by Char8 data.
+ /// Decoding treats as MBCS string.
+ /// </summary>
+ CountedMbcsString = 23,
+
+ /// <summary>
+ /// Core type.
+ /// Special case: Struct indicates that this field plus the the
+ /// subsequent N logical fields are to be considered as one logical
+ /// field (i.e. a nested structure). The OutType is used to encode N.
+ /// The maximum value for N is 127. This field has no payload by
+ /// itself, but logically contains the payload of the following N
+ /// fields. It is legal to have an array of Struct.
+ /// </summary>
+ Struct = 24,
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 16-bit value.
+ /// Decoding treats as UTF-16LE character.
+ /// </summary>
+ Char16 = UInt16 + (EventFieldFormat.String << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 8-bit value.
+ /// Decoding treats as character.
+ /// </summary>
+ Char8 = UInt8 + (EventFieldFormat.String << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 8-bit value.
+ /// Decoding treats as Boolean.
+ /// </summary>
+ Boolean8 = UInt8 + (EventFieldFormat.Boolean << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 8-bit value.
+ /// Decoding treats as hexadecimal unsigned integer.
+ /// </summary>
+ HexInt8 = UInt8 + (EventFieldFormat.Hexadecimal << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 16-bit value.
+ /// Decoding treats as hexadecimal unsigned integer.
+ /// </summary>
+ HexInt16 = UInt16 + (EventFieldFormat.Hexadecimal << 8),
+
+#if false
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as process identifier.
+ /// </summary>
+ ProcessId = UInt32 + (EventSourceFieldFormat.ProcessId << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as thread identifier.
+ /// </summary>
+ ThreadId = UInt32 + (EventSourceFieldFormat.ThreadId << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 16-bit value.
+ /// Decoding treats as IP port.
+ /// </summary>
+ Port = UInt16 + (EventSourceFieldFormat.Port << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as IPv4 address.
+ /// </summary>
+ Ipv4Address = UInt32 + (EventSourceFieldFormat.Ipv4Address << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 16-bit bytecount followed by binary data.
+ /// Decoding treats as IPv6 address.
+ /// </summary>
+ Ipv6Address = Binary + (EventSourceFieldFormat.Ipv6Address << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 16-bit bytecount followed by binary data.
+ /// Decoding treats as SOCKADDR.
+ /// </summary>
+ SocketAddress = Binary + (EventSourceFieldFormat.SocketAddress << 8),
+#endif
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes null-terminated Char16 string.
+ /// Decoding treats as UTF-16LE XML string.
+ /// </summary>
+ Utf16Xml = Utf16String + (EventFieldFormat.Xml << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes null-terminated Char8 string.
+ /// Decoding treats as MBCS XML string.
+ /// </summary>
+ MbcsXml = MbcsString + (EventFieldFormat.Xml << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 16-bit bytecount followed by Char16 data.
+ /// Decoding treats as UTF-16LE XML.
+ /// </summary>
+ CountedUtf16Xml = CountedUtf16String + (EventFieldFormat.Xml << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 16-bit bytecount followed by Char8 data.
+ /// Decoding treats as MBCS XML.
+ /// </summary>
+ CountedMbcsXml = CountedMbcsString + (EventFieldFormat.Xml << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes null-terminated Char16 string.
+ /// Decoding treats as UTF-16LE JSON string.
+ /// </summary>
+ Utf16Json = Utf16String + (EventFieldFormat.Json << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes null-terminated Char8 string.
+ /// Decoding treats as MBCS JSON string.
+ /// </summary>
+ MbcsJson = MbcsString + (EventFieldFormat.Json << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 16-bit bytecount followed by Char16 data.
+ /// Decoding treats as UTF-16LE JSON.
+ /// </summary>
+ CountedUtf16Json = CountedUtf16String + (EventFieldFormat.Json << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 16-bit bytecount followed by Char8 data.
+ /// Decoding treats as MBCS JSON.
+ /// </summary>
+ CountedMbcsJson = CountedMbcsString + (EventFieldFormat.Json << 8),
+#if false
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as Win32 error.
+ /// </summary>
+ Win32Error = UInt32 + (EventSourceFieldFormat.Win32Error << 8),
+
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as NTSTATUS.
+ /// </summary>
+ NTStatus = UInt32 + (EventSourceFieldFormat.NTStatus << 8),
+#endif
+ /// <summary>
+ /// Formatted type.
+ /// Encoding assumes 32-bit value.
+ /// Decoding treats as HRESULT.
+ /// </summary>
+ HResult = Int32 + (EventFieldFormat.HResult << 8)
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
new file mode 100644
index 0000000000..e73339949a
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventSource.cs
@@ -0,0 +1,890 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// This program uses code hyperlinks available as part of the HyperAddin Visual Studio plug-in.
+// It is available from http://www.codeplex.com/hyperAddin
+
+#if PLATFORM_WINDOWS
+#define FEATURE_MANAGED_ETW
+
+#if !ES_BUILD_STANDALONE
+#define FEATURE_ACTIVITYSAMPLING
+#endif
+#endif // PLATFORM_WINDOWS
+
+#if ES_BUILD_STANDALONE
+#define FEATURE_MANAGED_ETW_CHANNELS
+// #define FEATURE_ADVANCED_MANAGED_ETW_CHANNELS
+#endif
+
+#if ES_BUILD_STANDALONE
+using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
+using EventDescriptor = Microsoft.Diagnostics.Tracing.EventDescriptor;
+#endif
+
+using System;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Collections.ObjectModel;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+using System.Collections.Generic;
+using System.Text;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+using System.Collections.Generic;
+using System.Text;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ public partial class EventSource
+ {
+#if FEATURE_MANAGED_ETW
+ private byte[] providerMetadata;
+#endif
+
+ /// <summary>
+ /// Construct an EventSource with a given name for non-contract based events (e.g. those using the Write() API).
+ /// </summary>
+ /// <param name="eventSourceName">
+ /// The name of the event source. Must not be null.
+ /// </param>
+ public EventSource(
+ string eventSourceName)
+ : this(eventSourceName, EventSourceSettings.EtwSelfDescribingEventFormat)
+ { }
+
+ /// <summary>
+ /// Construct an EventSource with a given name for non-contract based events (e.g. those using the Write() API).
+ /// </summary>
+ /// <param name="eventSourceName">
+ /// The name of the event source. Must not be null.
+ /// </param>
+ /// <param name="config">
+ /// Configuration options for the EventSource as a whole.
+ /// </param>
+ public EventSource(
+ string eventSourceName,
+ EventSourceSettings config)
+ : this(eventSourceName, config, null) { }
+
+ /// <summary>
+ /// Construct an EventSource with a given name for non-contract based events (e.g. those using the Write() API).
+ ///
+ /// Also specify a list of key-value pairs called traits (you must pass an even number of strings).
+ /// The first string is the key and the second is the value. These are not interpreted by EventSource
+ /// itself but may be interprated the listeners. Can be fetched with GetTrait(string).
+ /// </summary>
+ /// <param name="eventSourceName">
+ /// The name of the event source. Must not be null.
+ /// </param>
+ /// <param name="config">
+ /// Configuration options for the EventSource as a whole.
+ /// </param>
+ /// <param name="traits">A collection of key-value strings (must be an even number).</param>
+ public EventSource(
+ string eventSourceName,
+ EventSourceSettings config,
+ params string[] traits)
+ : this(
+ eventSourceName == null ? new Guid() : GenerateGuidFromName(eventSourceName.ToUpperInvariant()),
+ eventSourceName,
+ config, traits)
+ {
+ if (eventSourceName == null)
+ {
+ throw new ArgumentNullException(nameof(eventSourceName));
+ }
+ Contract.EndContractBlock();
+ }
+
+ /// <summary>
+ /// Writes an event with no fields and default options.
+ /// (Native API: EventWriteTransfer)
+ /// </summary>
+ /// <param name="eventName">The name of the event. Must not be null.</param>
+ public unsafe void Write(string eventName)
+ {
+ if (eventName == null)
+ {
+ throw new ArgumentNullException(nameof(eventName));
+ }
+
+ Contract.EndContractBlock();
+
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ var options = new EventSourceOptions();
+ this.WriteImpl(eventName, ref options, null, null, null, SimpleEventTypes<EmptyStruct>.Instance);
+ }
+
+ /// <summary>
+ /// Writes an event with no fields.
+ /// (Native API: EventWriteTransfer)
+ /// </summary>
+ /// <param name="eventName">The name of the event. Must not be null.</param>
+ /// <param name="options">
+ /// Options for the event, such as the level, keywords, and opcode. Unset
+ /// options will be set to default values.
+ /// </param>
+ public unsafe void Write(string eventName, EventSourceOptions options)
+ {
+ if (eventName == null)
+ {
+ throw new ArgumentNullException(nameof(eventName));
+ }
+
+ Contract.EndContractBlock();
+
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ this.WriteImpl(eventName, ref options, null, null, null, SimpleEventTypes<EmptyStruct>.Instance);
+ }
+
+ /// <summary>
+ /// Writes an event.
+ /// (Native API: EventWriteTransfer)
+ /// </summary>
+ /// <typeparam name="T">
+ /// The type that defines the event and its payload. This must be an
+ /// anonymous type or a type with an [EventData] attribute.
+ /// </typeparam>
+ /// <param name="eventName">
+ /// The name for the event. If null, the event name is automatically
+ /// determined based on T, either from the Name property of T's EventData
+ /// attribute or from typeof(T).Name.
+ /// </param>
+ /// <param name="data">
+ /// The object containing the event payload data. The type T must be
+ /// an anonymous type or a type with an [EventData] attribute. The
+ /// public instance properties of data will be written recursively to
+ /// create the fields of the event.
+ /// </param>
+ public unsafe void Write<T>(
+ string eventName,
+ T data)
+ {
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ var options = new EventSourceOptions();
+ this.WriteImpl(eventName, ref options, data, null, null, SimpleEventTypes<T>.Instance);
+ }
+
+ /// <summary>
+ /// Writes an event.
+ /// (Native API: EventWriteTransfer)
+ /// </summary>
+ /// <typeparam name="T">
+ /// The type that defines the event and its payload. This must be an
+ /// anonymous type or a type with an [EventData] attribute.
+ /// </typeparam>
+ /// <param name="eventName">
+ /// The name for the event. If null, the event name is automatically
+ /// determined based on T, either from the Name property of T's EventData
+ /// attribute or from typeof(T).Name.
+ /// </param>
+ /// <param name="options">
+ /// Options for the event, such as the level, keywords, and opcode. Unset
+ /// options will be set to default values.
+ /// </param>
+ /// <param name="data">
+ /// The object containing the event payload data. The type T must be
+ /// an anonymous type or a type with an [EventData] attribute. The
+ /// public instance properties of data will be written recursively to
+ /// create the fields of the event.
+ /// </param>
+ public unsafe void Write<T>(
+ string eventName,
+ EventSourceOptions options,
+ T data)
+ {
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ this.WriteImpl(eventName, ref options, data, null, null, SimpleEventTypes<T>.Instance);
+ }
+
+ /// <summary>
+ /// Writes an event.
+ /// This overload is for use with extension methods that wish to efficiently
+ /// forward the options or data parameter without performing an extra copy.
+ /// (Native API: EventWriteTransfer)
+ /// </summary>
+ /// <typeparam name="T">
+ /// The type that defines the event and its payload. This must be an
+ /// anonymous type or a type with an [EventData] attribute.
+ /// </typeparam>
+ /// <param name="eventName">
+ /// The name for the event. If null, the event name is automatically
+ /// determined based on T, either from the Name property of T's EventData
+ /// attribute or from typeof(T).Name.
+ /// </param>
+ /// <param name="options">
+ /// Options for the event, such as the level, keywords, and opcode. Unset
+ /// options will be set to default values.
+ /// </param>
+ /// <param name="data">
+ /// The object containing the event payload data. The type T must be
+ /// an anonymous type or a type with an [EventData] attribute. The
+ /// public instance properties of data will be written recursively to
+ /// create the fields of the event.
+ /// </param>
+ public unsafe void Write<T>(
+ string eventName,
+ ref EventSourceOptions options,
+ ref T data)
+ {
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ this.WriteImpl(eventName, ref options, data, null, null, SimpleEventTypes<T>.Instance);
+ }
+
+ /// <summary>
+ /// Writes an event.
+ /// This overload is meant for clients that need to manipuate the activityId
+ /// and related ActivityId for the event.
+ /// </summary>
+ /// <typeparam name="T">
+ /// The type that defines the event and its payload. This must be an
+ /// anonymous type or a type with an [EventData] attribute.
+ /// </typeparam>
+ /// <param name="eventName">
+ /// The name for the event. If null, the event name is automatically
+ /// determined based on T, either from the Name property of T's EventData
+ /// attribute or from typeof(T).Name.
+ /// </param>
+ /// <param name="options">
+ /// Options for the event, such as the level, keywords, and opcode. Unset
+ /// options will be set to default values.
+ /// </param>
+ /// <param name="activityId">
+ /// The GUID of the activity associated with this event.
+ /// </param>
+ /// <param name="relatedActivityId">
+ /// The GUID of another activity that is related to this activity, or Guid.Empty
+ /// if there is no related activity. Most commonly, the Start operation of a
+ /// new activity specifies a parent activity as its related activity.
+ /// </param>
+ /// <param name="data">
+ /// The object containing the event payload data. The type T must be
+ /// an anonymous type or a type with an [EventData] attribute. The
+ /// public instance properties of data will be written recursively to
+ /// create the fields of the event.
+ /// </param>
+ public unsafe void Write<T>(
+ string eventName,
+ ref EventSourceOptions options,
+ ref Guid activityId,
+ ref Guid relatedActivityId,
+ ref T data)
+ {
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ fixed (Guid* pActivity = &activityId, pRelated = &relatedActivityId)
+ {
+ this.WriteImpl(
+ eventName,
+ ref options,
+ data,
+ pActivity,
+ relatedActivityId == Guid.Empty ? null : pRelated,
+ SimpleEventTypes<T>.Instance);
+ }
+ }
+
+ /// <summary>
+ /// Writes an extended event, where the values of the event are the
+ /// combined properties of any number of values. This method is
+ /// intended for use in advanced logging scenarios that support a
+ /// dynamic set of event context providers.
+ /// This method does a quick check on whether this event is enabled.
+ /// </summary>
+ /// <param name="eventName">
+ /// The name for the event. If null, the name from eventTypes is used.
+ /// (Note that providing the event name via the name parameter is slightly
+ /// less efficient than using the name from eventTypes.)
+ /// </param>
+ /// <param name="options">
+ /// Optional overrides for the event, such as the level, keyword, opcode,
+ /// activityId, and relatedActivityId. Any settings not specified by options
+ /// are obtained from eventTypes.
+ /// </param>
+ /// <param name="eventTypes">
+ /// Information about the event and the types of the values in the event.
+ /// Must not be null. Note that the eventTypes object should be created once and
+ /// saved. It should not be recreated for each event.
+ /// </param>
+ /// <param name="activityID">
+ /// A pointer to the activity ID GUID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// A pointer to the child activity ID to log (can be null) </param>
+ /// <param name="values">
+ /// The values to include in the event. Must not be null. The number and types of
+ /// the values must match the number and types of the fields described by the
+ /// eventTypes parameter.
+ /// </param>
+ private unsafe void WriteMultiMerge(
+ string eventName,
+ ref EventSourceOptions options,
+ TraceLoggingEventTypes eventTypes,
+ Guid* activityID,
+ Guid* childActivityID,
+ params object[] values)
+ {
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+ byte level = (options.valuesSet & EventSourceOptions.levelSet) != 0
+ ? options.level
+ : eventTypes.level;
+ EventKeywords keywords = (options.valuesSet & EventSourceOptions.keywordsSet) != 0
+ ? options.keywords
+ : eventTypes.keywords;
+
+ if (this.IsEnabled((EventLevel)level, keywords))
+ {
+ WriteMultiMergeInner(eventName, ref options, eventTypes, activityID, childActivityID, values);
+ }
+ }
+
+ /// <summary>
+ /// Writes an extended event, where the values of the event are the
+ /// combined properties of any number of values. This method is
+ /// intended for use in advanced logging scenarios that support a
+ /// dynamic set of event context providers.
+ /// Attention: This API does not check whether the event is enabled or not.
+ /// Please use WriteMultiMerge to avoid spending CPU cycles for events that are
+ /// not enabled.
+ /// </summary>
+ /// <param name="eventName">
+ /// The name for the event. If null, the name from eventTypes is used.
+ /// (Note that providing the event name via the name parameter is slightly
+ /// less efficient than using the name from eventTypes.)
+ /// </param>
+ /// <param name="options">
+ /// Optional overrides for the event, such as the level, keyword, opcode,
+ /// activityId, and relatedActivityId. Any settings not specified by options
+ /// are obtained from eventTypes.
+ /// </param>
+ /// <param name="eventTypes">
+ /// Information about the event and the types of the values in the event.
+ /// Must not be null. Note that the eventTypes object should be created once and
+ /// saved. It should not be recreated for each event.
+ /// </param>
+ /// <param name="activityID">
+ /// A pointer to the activity ID GUID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// A pointer to the child activity ID to log (can be null)
+ /// </param>
+ /// <param name="values">
+ /// The values to include in the event. Must not be null. The number and types of
+ /// the values must match the number and types of the fields described by the
+ /// eventTypes parameter.
+ /// </param>
+ private unsafe void WriteMultiMergeInner(
+ string eventName,
+ ref EventSourceOptions options,
+ TraceLoggingEventTypes eventTypes,
+ Guid* activityID,
+ Guid* childActivityID,
+ params object[] values)
+ {
+#if FEATURE_MANAGED_ETW
+ int identity = 0;
+ byte level = (options.valuesSet & EventSourceOptions.levelSet) != 0
+ ? options.level
+ : eventTypes.level;
+ byte opcode = (options.valuesSet & EventSourceOptions.opcodeSet) != 0
+ ? options.opcode
+ : eventTypes.opcode;
+ EventTags tags = (options.valuesSet & EventSourceOptions.tagsSet) != 0
+ ? options.tags
+ : eventTypes.Tags;
+ EventKeywords keywords = (options.valuesSet & EventSourceOptions.keywordsSet) != 0
+ ? options.keywords
+ : eventTypes.keywords;
+
+ var nameInfo = eventTypes.GetNameInfo(eventName ?? eventTypes.Name, tags);
+ if (nameInfo == null)
+ {
+ return;
+ }
+ identity = nameInfo.identity;
+ EventDescriptor descriptor = new EventDescriptor(identity, level, opcode, (long)keywords);
+
+ var pinCount = eventTypes.pinCount;
+ var scratch = stackalloc byte[eventTypes.scratchSize];
+ var descriptors = stackalloc EventData[eventTypes.dataCount + 3];
+ var pins = stackalloc GCHandle[pinCount];
+
+ fixed (byte*
+ pMetadata0 = this.providerMetadata,
+ pMetadata1 = nameInfo.nameMetadata,
+ pMetadata2 = eventTypes.typeMetadata)
+ {
+ descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2);
+ descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1);
+ descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1);
+
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+ System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
+#endif
+ try
+ {
+ DataCollector.ThreadInstance.Enable(
+ scratch,
+ eventTypes.scratchSize,
+ descriptors + 3,
+ eventTypes.dataCount,
+ pins,
+ pinCount);
+
+ for (int i = 0; i < eventTypes.typeInfos.Length; i++)
+ {
+ var info = eventTypes.typeInfos[i];
+ info.WriteData(TraceLoggingDataCollector.Instance, info.PropertyValueFactory(values[i]));
+ }
+
+ this.WriteEventRaw(
+ eventName,
+ ref descriptor,
+ activityID,
+ childActivityID,
+ (int)(DataCollector.ThreadInstance.Finish() - descriptors),
+ (IntPtr)descriptors);
+ }
+ finally
+ {
+ this.WriteCleanup(pins, pinCount);
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+
+ /// <summary>
+ /// Writes an extended event, where the values of the event have already
+ /// been serialized in "data".
+ /// </summary>
+ /// <param name="eventName">
+ /// The name for the event. If null, the name from eventTypes is used.
+ /// (Note that providing the event name via the name parameter is slightly
+ /// less efficient than using the name from eventTypes.)
+ /// </param>
+ /// <param name="options">
+ /// Optional overrides for the event, such as the level, keyword, opcode,
+ /// activityId, and relatedActivityId. Any settings not specified by options
+ /// are obtained from eventTypes.
+ /// </param>
+ /// <param name="eventTypes">
+ /// Information about the event and the types of the values in the event.
+ /// Must not be null. Note that the eventTypes object should be created once and
+ /// saved. It should not be recreated for each event.
+ /// </param>
+ /// <param name="activityID">
+ /// A pointer to the activity ID GUID to log
+ /// </param>
+ /// <param name="childActivityID">
+ /// A pointer to the child activity ID to log (can be null)
+ /// </param>
+ /// <param name="data">
+ /// The previously serialized values to include in the event. Must not be null.
+ /// The number and types of the values must match the number and types of the
+ /// fields described by the eventTypes parameter.
+ /// </param>
+ internal unsafe void WriteMultiMerge(
+ string eventName,
+ ref EventSourceOptions options,
+ TraceLoggingEventTypes eventTypes,
+ Guid* activityID,
+ Guid* childActivityID,
+ EventData* data)
+ {
+#if FEATURE_MANAGED_ETW
+ if (!this.IsEnabled())
+ {
+ return;
+ }
+
+ fixed (EventSourceOptions* pOptions = &options)
+ {
+ EventDescriptor descriptor;
+ var nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out descriptor);
+ if (nameInfo == null)
+ {
+ return;
+ }
+
+ // We make a descriptor for each EventData, and because we morph strings to counted strings
+ // we may have 2 for each arg, so we allocate enough for this.
+ var descriptors = stackalloc EventData[eventTypes.dataCount + eventTypes.typeInfos.Length * 2 + 3];
+
+ fixed (byte*
+ pMetadata0 = this.providerMetadata,
+ pMetadata1 = nameInfo.nameMetadata,
+ pMetadata2 = eventTypes.typeMetadata)
+ {
+ descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2);
+ descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1);
+ descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1);
+ int numDescrs = 3;
+
+ for (int i = 0; i < eventTypes.typeInfos.Length; i++)
+ {
+ // Until M3, we need to morph strings to a counted representation
+ // When TDH supports null terminated strings, we can remove this.
+ if (eventTypes.typeInfos[i].DataType == typeof(string))
+ {
+ // Write out the size of the string
+ descriptors[numDescrs].m_Ptr = (long)&descriptors[numDescrs + 1].m_Size;
+ descriptors[numDescrs].m_Size = 2;
+ numDescrs++;
+
+ descriptors[numDescrs].m_Ptr = data[i].m_Ptr;
+ descriptors[numDescrs].m_Size = data[i].m_Size - 2; // Remove the null terminator
+ numDescrs++;
+ }
+ else
+ {
+ descriptors[numDescrs].m_Ptr = data[i].m_Ptr;
+ descriptors[numDescrs].m_Size = data[i].m_Size;
+
+ // old conventions for bool is 4 bytes, but meta-data assumes 1.
+ if (data[i].m_Size == 4 && eventTypes.typeInfos[i].DataType == typeof(bool))
+ descriptors[numDescrs].m_Size = 1;
+
+ numDescrs++;
+ }
+ }
+
+ this.WriteEventRaw(
+ eventName,
+ ref descriptor,
+ activityID,
+ childActivityID,
+ numDescrs,
+ (IntPtr)descriptors);
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+
+ private unsafe void WriteImpl(
+ string eventName,
+ ref EventSourceOptions options,
+ object data,
+ Guid* pActivityId,
+ Guid* pRelatedActivityId,
+ TraceLoggingEventTypes eventTypes)
+ {
+ try
+ {
+ fixed (EventSourceOptions* pOptions = &options)
+ {
+ EventDescriptor descriptor;
+ options.Opcode = options.IsOpcodeSet ? options.Opcode : GetOpcodeWithDefault(options.Opcode, eventName);
+ var nameInfo = this.UpdateDescriptor(eventName, eventTypes, ref options, out descriptor);
+ if (nameInfo == null)
+ {
+ return;
+ }
+
+#if FEATURE_MANAGED_ETW
+ var pinCount = eventTypes.pinCount;
+ var scratch = stackalloc byte[eventTypes.scratchSize];
+ var descriptors = stackalloc EventData[eventTypes.dataCount + 3];
+ var pins = stackalloc GCHandle[pinCount];
+
+ fixed (byte*
+ pMetadata0 = this.providerMetadata,
+ pMetadata1 = nameInfo.nameMetadata,
+ pMetadata2 = eventTypes.typeMetadata)
+ {
+ descriptors[0].SetMetadata(pMetadata0, this.providerMetadata.Length, 2);
+ descriptors[1].SetMetadata(pMetadata1, nameInfo.nameMetadata.Length, 1);
+ descriptors[2].SetMetadata(pMetadata2, eventTypes.typeMetadata.Length, 1);
+#endif // FEATURE_MANAGED_ETW
+
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+ System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
+#endif
+ EventOpcode opcode = (EventOpcode)descriptor.Opcode;
+
+ Guid activityId = Guid.Empty;
+ Guid relatedActivityId = Guid.Empty;
+ if (pActivityId == null && pRelatedActivityId == null &&
+ ((options.ActivityOptions & EventActivityOptions.Disable) == 0))
+ {
+ if (opcode == EventOpcode.Start)
+ {
+ m_activityTracker.OnStart(m_name, eventName, 0, ref activityId, ref relatedActivityId, options.ActivityOptions);
+ }
+ else if (opcode == EventOpcode.Stop)
+ {
+ m_activityTracker.OnStop(m_name, eventName, 0, ref activityId);
+ }
+ if (activityId != Guid.Empty)
+ pActivityId = &activityId;
+ if (relatedActivityId != Guid.Empty)
+ pRelatedActivityId = &relatedActivityId;
+ }
+
+ try
+ {
+#if FEATURE_MANAGED_ETW
+ DataCollector.ThreadInstance.Enable(
+ scratch,
+ eventTypes.scratchSize,
+ descriptors + 3,
+ eventTypes.dataCount,
+ pins,
+ pinCount);
+
+ var info = eventTypes.typeInfos[0];
+ info.WriteData(TraceLoggingDataCollector.Instance, info.PropertyValueFactory(data));
+
+ this.WriteEventRaw(
+ eventName,
+ ref descriptor,
+ pActivityId,
+ pRelatedActivityId,
+ (int)(DataCollector.ThreadInstance.Finish() - descriptors),
+ (IntPtr)descriptors);
+#endif // FEATURE_MANAGED_ETW
+
+ // TODO enable filtering for listeners.
+ if (m_Dispatchers != null)
+ {
+ var eventData = (EventPayload)(eventTypes.typeInfos[0].GetData(data));
+ WriteToAllListeners(eventName, ref descriptor, nameInfo.tags, pActivityId, eventData);
+ }
+
+ }
+ catch (Exception ex)
+ {
+ if (ex is EventSourceException)
+ throw;
+ else
+ ThrowEventSourceException(eventName, ex);
+ }
+#if FEATURE_MANAGED_ETW
+ finally
+ {
+ this.WriteCleanup(pins, pinCount);
+ }
+ }
+#endif // FEATURE_MANAGED_ETW
+ }
+ }
+ catch (Exception ex)
+ {
+ if (ex is EventSourceException)
+ throw;
+ else
+ ThrowEventSourceException(eventName, ex);
+ }
+ }
+
+ private unsafe void WriteToAllListeners(string eventName, ref EventDescriptor eventDescriptor, EventTags tags, Guid* pActivityId, EventPayload payload)
+ {
+ EventWrittenEventArgs eventCallbackArgs = new EventWrittenEventArgs(this);
+ eventCallbackArgs.EventName = eventName;
+ eventCallbackArgs.m_level = (EventLevel) eventDescriptor.Level;
+ eventCallbackArgs.m_keywords = (EventKeywords) eventDescriptor.Keywords;
+ eventCallbackArgs.m_opcode = (EventOpcode) eventDescriptor.Opcode;
+ eventCallbackArgs.m_tags = tags;
+
+ // Self described events do not have an id attached. We mark it internally with -1.
+ eventCallbackArgs.EventId = -1;
+ if (pActivityId != null)
+ eventCallbackArgs.RelatedActivityId = *pActivityId;
+
+ if (payload != null)
+ {
+ eventCallbackArgs.Payload = new ReadOnlyCollection<object>((IList<object>)payload.Values);
+ eventCallbackArgs.PayloadNames = new ReadOnlyCollection<string>((IList<string>)payload.Keys);
+ }
+
+ DispatchToAllListeners(-1, pActivityId, eventCallbackArgs);
+ }
+
+#if (!ES_BUILD_PCL && !ES_BUILD_PN)
+ [System.Runtime.ConstrainedExecution.ReliabilityContract(
+ System.Runtime.ConstrainedExecution.Consistency.WillNotCorruptState,
+ System.Runtime.ConstrainedExecution.Cer.Success)]
+#endif
+ [NonEvent]
+ private unsafe void WriteCleanup(GCHandle* pPins, int cPins)
+ {
+ DataCollector.ThreadInstance.Disable();
+
+ for (int i = 0; i != cPins; i++)
+ {
+ if (IntPtr.Zero != (IntPtr)pPins[i])
+ {
+ pPins[i].Free();
+ }
+ }
+ }
+
+ private void InitializeProviderMetadata()
+ {
+#if FEATURE_MANAGED_ETW
+ if (m_traits != null)
+ {
+ List<byte> traitMetaData = new List<byte>(100);
+ for (int i = 0; i < m_traits.Length - 1; i += 2)
+ {
+ if (m_traits[i].StartsWith("ETW_", StringComparison.Ordinal))
+ {
+ string etwTrait = m_traits[i].Substring(4);
+ byte traitNum;
+ if (!byte.TryParse(etwTrait, out traitNum))
+ {
+ if (etwTrait == "GROUP")
+ {
+ traitNum = 1;
+ }
+ else
+ {
+ throw new ArgumentException(Resources.GetResourceString("UnknownEtwTrait", etwTrait), "traits");
+ }
+ }
+ string value = m_traits[i + 1];
+ int lenPos = traitMetaData.Count;
+ traitMetaData.Add(0); // Emit size (to be filled in later)
+ traitMetaData.Add(0);
+ traitMetaData.Add(traitNum); // Emit Trait number
+ var valueLen = AddValueToMetaData(traitMetaData, value) + 3; // Emit the value bytes +3 accounts for 3 bytes we emited above.
+ traitMetaData[lenPos] = unchecked((byte)valueLen); // Fill in size
+ traitMetaData[lenPos + 1] = unchecked((byte)(valueLen >> 8));
+ }
+ }
+ providerMetadata = Statics.MetadataForString(this.Name, 0, traitMetaData.Count, 0);
+ int startPos = providerMetadata.Length - traitMetaData.Count;
+ foreach (var b in traitMetaData)
+ providerMetadata[startPos++] = b;
+ }
+ else
+ providerMetadata = Statics.MetadataForString(this.Name, 0, 0, 0);
+#endif //FEATURE_MANAGED_ETW
+ }
+
+ private static int AddValueToMetaData(List<byte> metaData, string value)
+ {
+ if (value.Length == 0)
+ return 0;
+
+ int startPos = metaData.Count;
+ char firstChar = value[0];
+
+ if (firstChar == '@')
+ metaData.AddRange(Encoding.UTF8.GetBytes(value.Substring(1)));
+ else if (firstChar == '{')
+ metaData.AddRange(new Guid(value).ToByteArray());
+ else if (firstChar == '#')
+ {
+ for (int i = 1; i < value.Length; i++)
+ {
+ if (value[i] != ' ') // Skip spaces between bytes.
+ {
+ if (!(i + 1 < value.Length))
+ {
+ throw new ArgumentException(Resources.GetResourceString("EvenHexDigits"), "traits");
+ }
+ metaData.Add((byte)(HexDigit(value[i]) * 16 + HexDigit(value[i + 1])));
+ i++;
+ }
+ }
+ }
+ else if ('A' <= firstChar || ' ' == firstChar) // Is it alphabetic or space (excludes digits and most punctuation).
+ {
+ metaData.AddRange(Encoding.UTF8.GetBytes(value));
+ }
+ else
+ {
+ throw new ArgumentException(Resources.GetResourceString("IllegalValue", value), "traits");
+ }
+
+ return metaData.Count - startPos;
+ }
+
+ /// <summary>
+ /// Returns a value 0-15 if 'c' is a hexadecimal digit. If it throws an argument exception.
+ /// </summary>
+ private static int HexDigit(char c)
+ {
+ if ('0' <= c && c <= '9')
+ {
+ return (c - '0');
+ }
+ if ('a' <= c)
+ {
+ c = unchecked((char)(c - ('a' - 'A'))); // Convert to lower case
+ }
+ if ('A' <= c && c <= 'F')
+ {
+ return (c - 'A' + 10);
+ }
+
+ throw new ArgumentException(Resources.GetResourceString("BadHexDigit", c), "traits");
+ }
+
+ private NameInfo UpdateDescriptor(
+ string name,
+ TraceLoggingEventTypes eventInfo,
+ ref EventSourceOptions options,
+ out EventDescriptor descriptor)
+ {
+ NameInfo nameInfo = null;
+ int identity = 0;
+ byte level = (options.valuesSet & EventSourceOptions.levelSet) != 0
+ ? options.level
+ : eventInfo.level;
+ byte opcode = (options.valuesSet & EventSourceOptions.opcodeSet) != 0
+ ? options.opcode
+ : eventInfo.opcode;
+ EventTags tags = (options.valuesSet & EventSourceOptions.tagsSet) != 0
+ ? options.tags
+ : eventInfo.Tags;
+ EventKeywords keywords = (options.valuesSet & EventSourceOptions.keywordsSet) != 0
+ ? options.keywords
+ : eventInfo.keywords;
+
+ if (this.IsEnabled((EventLevel)level, keywords))
+ {
+ nameInfo = eventInfo.GetNameInfo(name ?? eventInfo.Name, tags);
+ identity = nameInfo.identity;
+ }
+
+ descriptor = new EventDescriptor(identity, level, opcode, (long)keywords);
+ return nameInfo;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs
new file mode 100644
index 0000000000..e808a8823c
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTraits.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// Tags are flags that are not interpreted by EventSource but are passed along
+ /// to the EventListener. The EventListener determines the semantics of the flags.
+ /// </summary>
+ [Flags]
+ public enum EventTags
+ {
+ /// <summary>
+ /// No special traits are added to the event.
+ /// </summary>
+ None = 0,
+
+ /* Bits below 0x10000 are available for any use by the provider. */
+ /* Bits at or above 0x10000 are reserved for definition by Microsoft. */
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs
new file mode 100644
index 0000000000..c2239671bb
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingEventTypes.cs
@@ -0,0 +1,262 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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 Interlocked = System.Threading.Interlocked;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: Used when calling EventSource.WriteMultiMerge.
+ /// Stores the type information to use when writing the event fields.
+ /// </summary>
+ public class TraceLoggingEventTypes
+ {
+ internal readonly TraceLoggingTypeInfo[] typeInfos;
+ internal readonly string name;
+ internal readonly EventTags tags;
+ internal readonly byte level;
+ internal readonly byte opcode;
+ internal readonly EventKeywords keywords;
+ internal readonly byte[] typeMetadata;
+ internal readonly int scratchSize;
+ internal readonly int dataCount;
+ internal readonly int pinCount;
+ private ConcurrentSet<KeyValuePair<string, EventTags>, NameInfo> nameInfos;
+
+ /// <summary>
+ /// Initializes a new instance of TraceLoggingEventTypes corresponding
+ /// to the name, flags, and types provided. Always uses the default
+ /// TypeInfo for each Type.
+ /// </summary>
+ /// <param name="name">
+ /// The name to use when the name parameter passed to
+ /// EventSource.Write is null. This value must not be null.
+ /// </param>
+ /// <param name="tags">
+ /// Tags to add to the event if the tags are not set via options.
+ /// </param>
+ /// <param name="types">
+ /// The types of the fields in the event. This value must not be null.
+ /// </param>
+ internal TraceLoggingEventTypes(
+ string name,
+ EventTags tags,
+ params Type[] types)
+ : this(tags, name, MakeArray(types))
+ {
+ return;
+ }
+
+ /// <summary>
+ /// Returns a new instance of TraceLoggingEventInfo corresponding to the name,
+ /// flags, and typeInfos provided.
+ /// </summary>
+ /// <param name="name">
+ /// The name to use when the name parameter passed to
+ /// EventSource.Write is null. This value must not be null.
+ /// </param>
+ /// <param name="tags">
+ /// Tags to add to the event if the tags are not set via options.
+ /// </param>
+ /// <param name="typeInfos">
+ /// The types of the fields in the event. This value must not be null.
+ /// </param>
+ /// <returns>
+ /// An instance of TraceLoggingEventInfo with DefaultName set to the specified name
+ /// and with the specified typeInfos.
+ /// </returns>
+ internal TraceLoggingEventTypes(
+ string name,
+ EventTags tags,
+ params TraceLoggingTypeInfo[] typeInfos)
+ : this(tags, name, MakeArray(typeInfos))
+ {
+ return;
+ }
+
+ internal TraceLoggingEventTypes(
+ string name,
+ EventTags tags,
+ System.Reflection.ParameterInfo[] paramInfos)
+ {
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Contract.EndContractBlock();
+
+ this.typeInfos = MakeArray(paramInfos);
+ this.name = name;
+ this.tags = tags;
+ this.level = Statics.DefaultLevel;
+
+ var collector = new TraceLoggingMetadataCollector();
+ for (int i = 0; i < typeInfos.Length; ++i)
+ {
+ var typeInfo = typeInfos[i];
+ this.level = Statics.Combine((int)typeInfo.Level, this.level);
+ this.opcode = Statics.Combine((int)typeInfo.Opcode, this.opcode);
+ this.keywords |= typeInfo.Keywords;
+ var paramName = paramInfos[i].Name;
+ if (Statics.ShouldOverrideFieldName(paramName))
+ {
+ paramName = typeInfo.Name;
+ }
+ typeInfo.WriteMetadata(collector, paramName, EventFieldFormat.Default);
+ }
+
+ this.typeMetadata = collector.GetMetadata();
+ this.scratchSize = collector.ScratchSize;
+ this.dataCount = collector.DataCount;
+ this.pinCount = collector.PinCount;
+ }
+
+ private TraceLoggingEventTypes(
+ EventTags tags,
+ string defaultName,
+ TraceLoggingTypeInfo[] typeInfos)
+ {
+ if (defaultName == null)
+ {
+ throw new ArgumentNullException(nameof(defaultName));
+ }
+
+ Contract.EndContractBlock();
+
+ this.typeInfos = typeInfos;
+ this.name = defaultName;
+ this.tags = tags;
+ this.level = Statics.DefaultLevel;
+
+ var collector = new TraceLoggingMetadataCollector();
+ foreach (var typeInfo in typeInfos)
+ {
+ this.level = Statics.Combine((int)typeInfo.Level, this.level);
+ this.opcode = Statics.Combine((int)typeInfo.Opcode, this.opcode);
+ this.keywords |= typeInfo.Keywords;
+ typeInfo.WriteMetadata(collector, null, EventFieldFormat.Default);
+ }
+
+ this.typeMetadata = collector.GetMetadata();
+ this.scratchSize = collector.ScratchSize;
+ this.dataCount = collector.DataCount;
+ this.pinCount = collector.PinCount;
+ }
+
+ /// <summary>
+ /// Gets the default name that will be used for events with this descriptor.
+ /// </summary>
+ internal string Name
+ {
+ get { return this.name; }
+ }
+
+ /// <summary>
+ /// Gets the default level that will be used for events with this descriptor.
+ /// </summary>
+ internal EventLevel Level
+ {
+ get { return (EventLevel)this.level; }
+ }
+
+ /// <summary>
+ /// Gets the default opcode that will be used for events with this descriptor.
+ /// </summary>
+ internal EventOpcode Opcode
+ {
+ get { return (EventOpcode)this.opcode; }
+ }
+
+ /// <summary>
+ /// Gets the default set of keywords that will added to events with this descriptor.
+ /// </summary>
+ internal EventKeywords Keywords
+ {
+ get { return (EventKeywords)this.keywords; }
+ }
+
+ /// <summary>
+ /// Gets the default tags that will be added events with this descriptor.
+ /// </summary>
+ internal EventTags Tags
+ {
+ get { return this.tags; }
+ }
+
+ internal NameInfo GetNameInfo(string name, EventTags tags)
+ {
+ var ret = this.nameInfos.TryGet(new KeyValuePair<string, EventTags>(name, tags));
+ if (ret == null)
+ {
+ ret = this.nameInfos.GetOrAdd(new NameInfo(name, tags, this.typeMetadata.Length));
+ }
+
+ return ret;
+ }
+
+ private TraceLoggingTypeInfo[] MakeArray(System.Reflection.ParameterInfo[] paramInfos)
+ {
+ if (paramInfos == null)
+ {
+ throw new ArgumentNullException(nameof(paramInfos));
+ }
+
+ Contract.EndContractBlock();
+
+ var recursionCheck = new List<Type>(paramInfos.Length);
+ var result = new TraceLoggingTypeInfo[paramInfos.Length];
+ for (int i = 0; i < paramInfos.Length; ++i)
+ {
+ result[i] = TraceLoggingTypeInfo.GetInstance(paramInfos[i].ParameterType, recursionCheck);
+ }
+
+ return result;
+ }
+
+ private static TraceLoggingTypeInfo[] MakeArray(Type[] types)
+ {
+ if (types == null)
+ {
+ throw new ArgumentNullException(nameof(types));
+ }
+
+ Contract.EndContractBlock();
+
+ var recursionCheck = new List<Type>(types.Length);
+ var result = new TraceLoggingTypeInfo[types.Length];
+ for (int i = 0; i < types.Length; i++)
+ {
+ result[i] = TraceLoggingTypeInfo.GetInstance(types[i], recursionCheck);
+ }
+
+ return result;
+ }
+
+ private static TraceLoggingTypeInfo[] MakeArray(
+ TraceLoggingTypeInfo[] typeInfos)
+ {
+ if (typeInfos == null)
+ {
+ throw new ArgumentNullException(nameof(typeInfos));
+ }
+
+ Contract.EndContractBlock();
+
+ return (TraceLoggingTypeInfo[])typeInfos.Clone(); ;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs
new file mode 100644
index 0000000000..41225c8626
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingMetadataCollector.cs
@@ -0,0 +1,370 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if ES_BUILD_STANDALONE
+using Environment = Microsoft.Diagnostics.Tracing.Internal.Environment;
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: used when implementing a custom TraceLoggingTypeInfo.
+ /// An instance of this type is provided to the TypeInfo.WriteMetadata method.
+ /// </summary>
+ internal class TraceLoggingMetadataCollector
+ {
+ private readonly Impl impl;
+ private readonly FieldMetadata currentGroup;
+ private int bufferedArrayFieldCount = int.MinValue;
+
+ /// <summary>
+ /// Creates a root-level collector.
+ /// </summary>
+ internal TraceLoggingMetadataCollector()
+ {
+ this.impl = new Impl();
+ }
+
+ /// <summary>
+ /// Creates a collector for a group.
+ /// </summary>
+ /// <param name="other">Parent collector</param>
+ /// <param name="group">The field that starts the group</param>
+ private TraceLoggingMetadataCollector(
+ TraceLoggingMetadataCollector other,
+ FieldMetadata group)
+ {
+ this.impl = other.impl;
+ this.currentGroup = group;
+ }
+
+ /// <summary>
+ /// The field tags to be used for the next field.
+ /// This will be reset to None each time a field is written.
+ /// </summary>
+ internal EventFieldTags Tags
+ {
+ get;
+ set;
+ }
+
+ internal int ScratchSize
+ {
+ get { return this.impl.scratchSize; }
+ }
+
+ internal int DataCount
+ {
+ get { return this.impl.dataCount; }
+ }
+
+ internal int PinCount
+ {
+ get { return this.impl.pinCount; }
+ }
+
+ private bool BeginningBufferedArray
+ {
+ get { return this.bufferedArrayFieldCount == 0; }
+ }
+
+ /// <summary>
+ /// Call this method to add a group to the event and to return
+ /// a new metadata collector that can be used to add fields to the
+ /// group. After all of the fields in the group have been written,
+ /// switch back to the original metadata collector to add fields
+ /// outside of the group.
+ /// Special-case: if name is null, no group is created, and AddGroup
+ /// returns the original metadata collector. This is useful when
+ /// adding the top-level group for an event.
+ /// Note: do not use the original metadata collector while the group's
+ /// metadata collector is in use, and do not use the group's metadata
+ /// collector after switching back to the original.
+ /// </summary>
+ /// <param name="name">
+ /// The name of the group. If name is null, the call to AddGroup is a
+ /// no-op (collector.AddGroup(null) returns collector).
+ /// </param>
+ /// <returns>
+ /// A new metadata collector that can be used to add fields to the group.
+ /// </returns>
+ public TraceLoggingMetadataCollector AddGroup(string name)
+ {
+ TraceLoggingMetadataCollector result = this;
+
+ if (name != null || // Normal.
+ this.BeginningBufferedArray) // Error, FieldMetadata's constructor will throw the appropriate exception.
+ {
+ var newGroup = new FieldMetadata(
+ name,
+ TraceLoggingDataType.Struct,
+ this.Tags,
+ this.BeginningBufferedArray);
+ this.AddField(newGroup);
+ result = new TraceLoggingMetadataCollector(this, newGroup);
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Adds a scalar field to an event.
+ /// </summary>
+ /// <param name="name">
+ /// The name to use for the added field. This value must not be null.
+ /// </param>
+ /// <param name="type">
+ /// The type code for the added field. This must be a fixed-size type
+ /// (e.g. string types are not supported).
+ /// </param>
+ public void AddScalar(string name, TraceLoggingDataType type)
+ {
+ int size;
+ switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask))
+ {
+ case TraceLoggingDataType.Int8:
+ case TraceLoggingDataType.UInt8:
+ case TraceLoggingDataType.Char8:
+ size = 1;
+ break;
+ case TraceLoggingDataType.Int16:
+ case TraceLoggingDataType.UInt16:
+ case TraceLoggingDataType.Char16:
+ size = 2;
+ break;
+ case TraceLoggingDataType.Int32:
+ case TraceLoggingDataType.UInt32:
+ case TraceLoggingDataType.HexInt32:
+ case TraceLoggingDataType.Float:
+ case TraceLoggingDataType.Boolean32:
+ size = 4;
+ break;
+ case TraceLoggingDataType.Int64:
+ case TraceLoggingDataType.UInt64:
+ case TraceLoggingDataType.HexInt64:
+ case TraceLoggingDataType.Double:
+ case TraceLoggingDataType.FileTime:
+ size = 8;
+ break;
+ case TraceLoggingDataType.Guid:
+ case TraceLoggingDataType.SystemTime:
+ size = 16;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(type));
+ }
+
+ this.impl.AddScalar(size);
+ this.AddField(new FieldMetadata(name, type, this.Tags, this.BeginningBufferedArray));
+ }
+
+ /// <summary>
+ /// Adds a binary-format field to an event.
+ /// Compatible with core types: Binary, CountedUtf16String, CountedMbcsString.
+ /// Compatible with dataCollector methods: AddBinary(string), AddArray(Any8bitType[]).
+ /// </summary>
+ /// <param name="name">
+ /// The name to use for the added field. This value must not be null.
+ /// </param>
+ /// <param name="type">
+ /// The type code for the added field. This must be a Binary or CountedString type.
+ /// </param>
+ public void AddBinary(string name, TraceLoggingDataType type)
+ {
+ switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask))
+ {
+ case TraceLoggingDataType.Binary:
+ case TraceLoggingDataType.CountedMbcsString:
+ case TraceLoggingDataType.CountedUtf16String:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(type));
+ }
+
+ this.impl.AddScalar(2);
+ this.impl.AddNonscalar();
+ this.AddField(new FieldMetadata(name, type, this.Tags, this.BeginningBufferedArray));
+ }
+
+ /// <summary>
+ /// Adds an array field to an event.
+ /// </summary>
+ /// <param name="name">
+ /// The name to use for the added field. This value must not be null.
+ /// </param>
+ /// <param name="type">
+ /// The type code for the added field. This must be a fixed-size type
+ /// or a string type. In the case of a string type, this adds an array
+ /// of characters, not an array of strings.
+ /// </param>
+ public void AddArray(string name, TraceLoggingDataType type)
+ {
+ switch ((TraceLoggingDataType)((int)type & Statics.InTypeMask))
+ {
+ case TraceLoggingDataType.Utf16String:
+ case TraceLoggingDataType.MbcsString:
+ case TraceLoggingDataType.Int8:
+ case TraceLoggingDataType.UInt8:
+ case TraceLoggingDataType.Int16:
+ case TraceLoggingDataType.UInt16:
+ case TraceLoggingDataType.Int32:
+ case TraceLoggingDataType.UInt32:
+ case TraceLoggingDataType.Int64:
+ case TraceLoggingDataType.UInt64:
+ case TraceLoggingDataType.Float:
+ case TraceLoggingDataType.Double:
+ case TraceLoggingDataType.Boolean32:
+ case TraceLoggingDataType.Guid:
+ case TraceLoggingDataType.FileTime:
+ case TraceLoggingDataType.HexInt32:
+ case TraceLoggingDataType.HexInt64:
+ case TraceLoggingDataType.Char16:
+ case TraceLoggingDataType.Char8:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(type));
+ }
+
+ if (this.BeginningBufferedArray)
+ {
+ throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedNestedArraysEnums"));
+ }
+
+ this.impl.AddScalar(2);
+ this.impl.AddNonscalar();
+ this.AddField(new FieldMetadata(name, type, this.Tags, true));
+ }
+
+ public void BeginBufferedArray()
+ {
+ if (this.bufferedArrayFieldCount >= 0)
+ {
+ throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedNestedArraysEnums"));
+ }
+
+ this.bufferedArrayFieldCount = 0;
+ this.impl.BeginBuffered();
+ }
+
+ public void EndBufferedArray()
+ {
+ if (this.bufferedArrayFieldCount != 1)
+ {
+ throw new InvalidOperationException(Resources.GetResourceString("EventSource_IncorrentlyAuthoredTypeInfo"));
+ }
+
+ this.bufferedArrayFieldCount = int.MinValue;
+ this.impl.EndBuffered();
+ }
+
+ /// <summary>
+ /// Adds a custom-serialized field to an event.
+ /// </summary>
+ /// <param name="name">
+ /// The name to use for the added field. This value must not be null.
+ /// </param>
+ /// <param name="type">The encoding type for the field.</param>
+ /// <param name="metadata">Additional information needed to decode the field, if any.</param>
+ public void AddCustom(string name, TraceLoggingDataType type, byte[] metadata)
+ {
+ if (this.BeginningBufferedArray)
+ {
+ throw new NotSupportedException(Resources.GetResourceString("EventSource_NotSupportedCustomSerializedData"));
+ }
+
+ this.impl.AddScalar(2);
+ this.impl.AddNonscalar();
+ this.AddField(new FieldMetadata(
+ name,
+ type,
+ this.Tags,
+ metadata));
+ }
+
+ internal byte[] GetMetadata()
+ {
+ var size = this.impl.Encode(null);
+ var metadata = new byte[size];
+ this.impl.Encode(metadata);
+ return metadata;
+ }
+
+ private void AddField(FieldMetadata fieldMetadata)
+ {
+ this.Tags = EventFieldTags.None;
+ this.bufferedArrayFieldCount++;
+ this.impl.fields.Add(fieldMetadata);
+
+ if (this.currentGroup != null)
+ {
+ this.currentGroup.IncrementStructFieldCount();
+ }
+ }
+
+ private class Impl
+ {
+ internal readonly List<FieldMetadata> fields = new List<FieldMetadata>();
+ internal short scratchSize;
+ internal sbyte dataCount;
+ internal sbyte pinCount;
+ private int bufferNesting;
+ private bool scalar;
+
+ public void AddScalar(int size)
+ {
+ if (this.bufferNesting == 0)
+ {
+ if (!this.scalar)
+ {
+ this.dataCount = checked((sbyte)(this.dataCount + 1));
+ }
+
+ this.scalar = true;
+ this.scratchSize = checked((short)(this.scratchSize + size));
+ }
+ }
+
+ public void AddNonscalar()
+ {
+ if (this.bufferNesting == 0)
+ {
+ this.scalar = false;
+ this.pinCount = checked((sbyte)(this.pinCount + 1));
+ this.dataCount = checked((sbyte)(this.dataCount + 1));
+ }
+ }
+
+ public void BeginBuffered()
+ {
+ if (this.bufferNesting == 0)
+ {
+ this.AddNonscalar();
+ }
+
+ this.bufferNesting++;
+ }
+
+ public void EndBuffered()
+ {
+ this.bufferNesting--;
+ }
+
+ public int Encode(byte[] metadata)
+ {
+ int size = 0;
+
+ foreach (var field in this.fields)
+ {
+ field.Encode(ref size, metadata);
+ }
+
+ return size;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs
new file mode 100644
index 0000000000..d68e106b0b
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TraceLoggingTypeInfo.cs
@@ -0,0 +1,209 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+#if !ES_BUILD_AGAINST_DOTNET_V35
+using Contract = System.Diagnostics.Contracts.Contract;
+#else
+using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract;
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: used when implementing a custom TraceLoggingTypeInfo.
+ /// Non-generic base class for TraceLoggingTypeInfo&lt;DataType>. Do not derive
+ /// from this class. Instead, derive from TraceLoggingTypeInfo&lt;DataType>.
+ /// </summary>
+ internal abstract class TraceLoggingTypeInfo
+ {
+ private readonly string name;
+ private readonly EventKeywords keywords;
+ private readonly EventLevel level = (EventLevel)(-1);
+ private readonly EventOpcode opcode = (EventOpcode)(-1);
+ private readonly EventTags tags;
+ private readonly Type dataType;
+ private readonly Func<object, PropertyValue> propertyValueFactory;
+
+ internal TraceLoggingTypeInfo(Type dataType)
+ {
+ if (dataType == null)
+ {
+ throw new ArgumentNullException(nameof(dataType));
+ }
+
+ Contract.EndContractBlock();
+
+ this.name = dataType.Name;
+ this.dataType = dataType;
+ this.propertyValueFactory = PropertyValue.GetFactory(dataType);
+ }
+
+ internal TraceLoggingTypeInfo(
+ Type dataType,
+ string name,
+ EventLevel level,
+ EventOpcode opcode,
+ EventKeywords keywords,
+ EventTags tags)
+ {
+ if (dataType == null)
+ {
+ throw new ArgumentNullException(nameof(dataType));
+ }
+
+ if (name == null)
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ Contract.EndContractBlock();
+
+ Statics.CheckName(name);
+
+ this.name = name;
+ this.keywords = keywords;
+ this.level = level;
+ this.opcode = opcode;
+ this.tags = tags;
+ this.dataType = dataType;
+ this.propertyValueFactory = PropertyValue.GetFactory(dataType);
+ }
+
+ /// <summary>
+ /// Gets the name to use for the event if this type is the top-level type,
+ /// or the name to use for an implicitly-named field.
+ /// Never null.
+ /// </summary>
+ public string Name
+ {
+ get { return this.name; }
+ }
+
+ /// <summary>
+ /// Gets the event level associated with this type. Any value in the range 0..255
+ /// is an associated event level. Any value outside the range 0..255 is invalid and
+ /// indicates that this type has no associated event level.
+ /// </summary>
+ public EventLevel Level
+ {
+ get { return this.level; }
+ }
+
+ /// <summary>
+ /// Gets the event opcode associated with this type. Any value in the range 0..255
+ /// is an associated event opcode. Any value outside the range 0..255 is invalid and
+ /// indicates that this type has no associated event opcode.
+ /// </summary>
+ public EventOpcode Opcode
+ {
+ get { return this.opcode; }
+ }
+
+ /// <summary>
+ /// Gets the keyword(s) associated with this type.
+ /// </summary>
+ public EventKeywords Keywords
+ {
+ get { return this.keywords; }
+ }
+
+ /// <summary>
+ /// Gets the event tags associated with this type.
+ /// </summary>
+ public EventTags Tags
+ {
+ get { return this.tags; }
+ }
+
+ internal Type DataType
+ {
+ get { return this.dataType; }
+ }
+
+ internal Func<object, PropertyValue> PropertyValueFactory
+ {
+ get { return this.propertyValueFactory; }
+ }
+
+ /// <summary>
+ /// When overridden by a derived class, writes the metadata (schema) for
+ /// this type. Note that the sequence of operations in WriteMetadata should be
+ /// essentially identical to the sequence of operations in
+ /// WriteData/WriteObjectData. Otherwise, the metadata and data will not match,
+ /// which may cause trouble when decoding the event.
+ /// </summary>
+ /// <param name="collector">
+ /// The object that collects metadata for this object's type. Metadata is written
+ /// by calling methods on the collector object. Note that if the type contains
+ /// sub-objects, the implementation of this method may need to call the
+ /// WriteMetadata method for the type of the sub-object, e.g. by calling
+ /// TraceLoggingTypeInfo&lt;SubType&gt;.Instance.WriteMetadata(...).
+ /// </param>
+ /// <param name="name">
+ /// The name of the property that contains an object of this type, or null if this
+ /// object is being written as a top-level object of an event. Typical usage
+ /// is to pass this value to collector.AddGroup.
+ /// </param>
+ /// <param name="format">
+ /// The format attribute for the field that contains an object of this type.
+ /// </param>
+ public abstract void WriteMetadata(
+ TraceLoggingMetadataCollector collector,
+ string name,
+ EventFieldFormat format);
+
+ /// <summary>
+ /// Refer to TraceLoggingTypeInfo.WriteObjectData for information about this
+ /// method.
+ /// </summary>
+ /// <param name="collector">
+ /// Refer to TraceLoggingTypeInfo.WriteObjectData for information about this
+ /// method.
+ /// </param>
+ /// <param name="value">
+ /// Refer to TraceLoggingTypeInfo.WriteObjectData for information about this
+ /// method.
+ /// </param>
+ public abstract void WriteData(
+ TraceLoggingDataCollector collector,
+ PropertyValue value);
+
+ /// <summary>
+ /// Fetches the event parameter data for internal serialization.
+ /// </summary>
+ /// <param name="value"></param>
+ /// <returns></returns>
+ public virtual object GetData(object value)
+ {
+ return value;
+ }
+
+ [ThreadStatic] // per-thread cache to avoid synchronization
+ private static Dictionary<Type, TraceLoggingTypeInfo> threadCache;
+
+ public static TraceLoggingTypeInfo GetInstance(Type type, List<Type> recursionCheck)
+ {
+ var cache = threadCache ?? (threadCache = new Dictionary<Type, TraceLoggingTypeInfo>());
+
+ TraceLoggingTypeInfo instance;
+ if (!cache.TryGetValue(type, out instance))
+ {
+ if (recursionCheck == null)
+ recursionCheck = new List<Type>();
+ var recursionCheckCount = recursionCheck.Count;
+ instance = Statics.CreateDefaultTypeInfo(type, recursionCheck);
+ cache[type] = instance;
+ recursionCheck.RemoveRange(recursionCheckCount, recursionCheck.Count - recursionCheckCount);
+ }
+ return instance;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs
new file mode 100644
index 0000000000..42cdde5f6a
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/TypeAnalysis.cs
@@ -0,0 +1,103 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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 System.Reflection;
+
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ /// <summary>
+ /// TraceLogging: stores the per-type information obtained by reflecting over a type.
+ /// </summary>
+ internal sealed class TypeAnalysis
+ {
+ internal readonly PropertyAnalysis[] properties;
+ internal readonly string name;
+ internal readonly EventKeywords keywords;
+ internal readonly EventLevel level = (EventLevel)(-1);
+ internal readonly EventOpcode opcode = (EventOpcode)(-1);
+ internal readonly EventTags tags;
+
+ public TypeAnalysis(
+ Type dataType,
+ EventDataAttribute eventAttrib,
+ List<Type> recursionCheck)
+ {
+ var propertyInfos = Statics.GetProperties(dataType);
+ var propertyList = new List<PropertyAnalysis>();
+
+ foreach (var propertyInfo in propertyInfos)
+ {
+ if (Statics.HasCustomAttribute(propertyInfo, typeof(EventIgnoreAttribute)))
+ {
+ continue;
+ }
+
+ if (!propertyInfo.CanRead ||
+ propertyInfo.GetIndexParameters().Length != 0)
+ {
+ continue;
+ }
+
+ MethodInfo getterInfo = Statics.GetGetMethod(propertyInfo);
+ if (getterInfo == null)
+ {
+ continue;
+ }
+
+ if (getterInfo.IsStatic || !getterInfo.IsPublic)
+ {
+ continue;
+ }
+
+ var propertyType = propertyInfo.PropertyType;
+ var propertyTypeInfo = TraceLoggingTypeInfo.GetInstance(propertyType, recursionCheck);
+ var fieldAttribute = Statics.GetCustomAttribute<EventFieldAttribute>(propertyInfo);
+
+ string propertyName =
+ fieldAttribute != null && fieldAttribute.Name != null
+ ? fieldAttribute.Name
+ : Statics.ShouldOverrideFieldName(propertyInfo.Name)
+ ? propertyTypeInfo.Name
+ : propertyInfo.Name;
+ propertyList.Add(new PropertyAnalysis(
+ propertyName,
+ propertyInfo,
+ propertyTypeInfo,
+ fieldAttribute));
+ }
+
+ this.properties = propertyList.ToArray();
+
+ foreach (var property in this.properties)
+ {
+ var typeInfo = property.typeInfo;
+ this.level = (EventLevel)Statics.Combine((int)typeInfo.Level, (int)this.level);
+ this.opcode = (EventOpcode)Statics.Combine((int)typeInfo.Opcode, (int)this.opcode);
+ this.keywords |= typeInfo.Keywords;
+ this.tags |= typeInfo.Tags;
+ }
+
+ if (eventAttrib != null)
+ {
+ this.level = (EventLevel)Statics.Combine((int)eventAttrib.Level, (int)this.level);
+ this.opcode = (EventOpcode)Statics.Combine((int)eventAttrib.Opcode, (int)this.opcode);
+ this.keywords |= eventAttrib.Keywords;
+ this.tags |= eventAttrib.Tags;
+ this.name = eventAttrib.Name;
+ }
+
+ if (this.name == null)
+ {
+ this.name = dataType.Name;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/Winmeta.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/Winmeta.cs
new file mode 100644
index 0000000000..ac756b6059
--- /dev/null
+++ b/src/mscorlib/shared/System/Diagnostics/Tracing/Winmeta.cs
@@ -0,0 +1,196 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+** Purpose:
+** Contains eventing constants defined by the Windows
+** environment.
+**
+============================================================*/
+#if ES_BUILD_STANDALONE
+#define FEATURE_MANAGED_ETW_CHANNELS
+#endif
+
+#if ES_BUILD_STANDALONE
+namespace Microsoft.Diagnostics.Tracing
+#else
+namespace System.Diagnostics.Tracing
+#endif
+{
+ using System;
+
+ /// <summary>
+ /// WindowsEventLevel. Custom values must be in the range from 16 through 255
+ /// </summary>
+ public enum EventLevel
+ {
+ /// <summary>
+ /// Log always
+ /// </summary>
+ LogAlways = 0,
+ /// <summary>
+ /// Only critical errors
+ /// </summary>
+ Critical,
+ /// <summary>
+ /// All errors, including previous levels
+ /// </summary>
+ Error,
+ /// <summary>
+ /// All warnings, including previous levels
+ /// </summary>
+ Warning,
+ /// <summary>
+ /// All informational events, including previous levels
+ /// </summary>
+ Informational,
+ /// <summary>
+ /// All events, including previous levels
+ /// </summary>
+ Verbose
+ }
+ /// <summary>
+ /// WindowsEventTask. Custom values must be in the range from 1 through 65534
+ /// </summary>
+#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
+ [System.Runtime.CompilerServices.FriendAccessAllowed]
+#endif
+ public enum EventTask
+ {
+ /// <summary>
+ /// Undefined task
+ /// </summary>
+ None = 0
+ }
+ /// <summary>
+ /// EventOpcode. Custom values must be in the range from 11 through 239
+ /// </summary>
+#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
+ [System.Runtime.CompilerServices.FriendAccessAllowed]
+#endif
+ public enum EventOpcode
+ {
+ /// <summary>
+ /// An informational event
+ /// </summary>
+ Info = 0,
+ /// <summary>
+ /// An activity start event
+ /// </summary>
+ Start,
+ /// <summary>
+ /// An activity end event
+ /// </summary>
+ Stop,
+ /// <summary>
+ /// A trace collection start event
+ /// </summary>
+ DataCollectionStart,
+ /// <summary>
+ /// A trace collection end event
+ /// </summary>
+ DataCollectionStop,
+ /// <summary>
+ /// An extensional event
+ /// </summary>
+ Extension,
+ /// <summary>
+ /// A reply event
+ /// </summary>
+ Reply,
+ /// <summary>
+ /// An event representing the activity resuming from the suspension
+ /// </summary>
+ Resume,
+ /// <summary>
+ /// An event representing the activity is suspended, pending another activity's completion
+ /// </summary>
+ Suspend,
+ /// <summary>
+ /// An event representing the activity is transferred to another component, and can continue to work
+ /// </summary>
+ Send,
+ /// <summary>
+ /// An event representing receiving an activity transfer from another component
+ /// </summary>
+ Receive = 240
+ }
+
+ // Added for CLR V4
+ /// <summary>
+ /// EventChannel. Custom values must be in the range from 16 through 255. Currently only predefined values allowed.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1028:EnumStorageShouldBeInt32", Justification = "Backwards compatibility")]
+#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
+ [System.Runtime.CompilerServices.FriendAccessAllowed]
+#endif
+ public enum EventChannel : byte
+ {
+ /// <summary>
+ /// No channel
+ /// </summary>
+ None = 0,
+ // Channels 1 - 15 are reserved...
+ /// <summary>The admin channel</summary>
+ Admin = 16,
+ /// <summary>The operational channel</summary>
+ Operational = 17,
+ /// <summary>The analytic channel</summary>
+ Analytic = 18,
+ /// <summary>The debug channel</summary>
+ Debug = 19,
+
+ };
+
+ /// <summary>
+ /// EventOpcode
+ /// </summary>
+ [Flags]
+ public enum EventKeywords : long
+ {
+ /// <summary>
+ /// No events.
+ /// </summary>
+ None = 0x0,
+ /// <summary>
+ /// All Events
+ /// </summary>
+ All = ~0,
+ /// <summary>
+ /// Telemetry events
+ /// </summary>
+ MicrosoftTelemetry = 0x02000000000000,
+ /// <summary>
+ /// WDI context events
+ /// </summary>
+ WdiContext = 0x02000000000000,
+ /// <summary>
+ /// WDI diagnostic events
+ /// </summary>
+ WdiDiagnostic = 0x04000000000000,
+ /// <summary>
+ /// SQM events
+ /// </summary>
+ Sqm = 0x08000000000000,
+ /// <summary>
+ /// Failed security audits
+ /// </summary>
+ AuditFailure = 0x10000000000000,
+ /// <summary>
+ /// Successful security audits
+ /// </summary>
+ AuditSuccess = 0x20000000000000,
+ /// <summary>
+ /// Transfer events where the related Activity ID is a computed value and not a GUID
+ /// N.B. The correct value for this field is 0x40000000000000.
+ /// </summary>
+ CorrelationHint = 0x10000000000000,
+ /// <summary>
+ /// Events raised using classic eventlog API
+ /// </summary>
+ EventLogClassic = 0x80000000000000
+ }
+}
diff --git a/src/mscorlib/shared/System/DivideByZeroException.cs b/src/mscorlib/shared/System/DivideByZeroException.cs
new file mode 100644
index 0000000000..4abd43adaf
--- /dev/null
+++ b/src/mscorlib/shared/System/DivideByZeroException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for bad arithmetic conditions!
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class DivideByZeroException : ArithmeticException
+ {
+ public DivideByZeroException()
+ : base(SR.Arg_DivideByZero)
+ {
+ HResult = __HResults.COR_E_DIVIDEBYZERO;
+ }
+
+ public DivideByZeroException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_DIVIDEBYZERO;
+ }
+
+ public DivideByZeroException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_DIVIDEBYZERO;
+ }
+
+ protected DivideByZeroException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/DuplicateWaitObjectException.cs b/src/mscorlib/shared/System/DuplicateWaitObjectException.cs
new file mode 100644
index 0000000000..da29e2ad76
--- /dev/null
+++ b/src/mscorlib/shared/System/DuplicateWaitObjectException.cs
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for duplicate objects in WaitAll/WaitAny.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ // The DuplicateWaitObjectException is thrown when an object
+ // appears more than once in the list of objects to WaitAll or WaitAny.
+ //
+ [Serializable]
+ public class DuplicateWaitObjectException : ArgumentException
+ {
+ private static volatile String s_duplicateWaitObjectMessage = null;
+
+ private static String DuplicateWaitObjectMessage
+ {
+ get
+ {
+ if (s_duplicateWaitObjectMessage == null)
+ s_duplicateWaitObjectMessage = SR.Arg_DuplicateWaitObjectException;
+ return s_duplicateWaitObjectMessage;
+ }
+ }
+
+ // Creates a new DuplicateWaitObjectException with its message
+ // string set to a default message.
+ public DuplicateWaitObjectException()
+ : base(DuplicateWaitObjectMessage)
+ {
+ HResult = __HResults.COR_E_DUPLICATEWAITOBJECT;
+ }
+
+ public DuplicateWaitObjectException(String parameterName)
+ : base(DuplicateWaitObjectMessage, parameterName)
+ {
+ HResult = __HResults.COR_E_DUPLICATEWAITOBJECT;
+ }
+
+ public DuplicateWaitObjectException(String parameterName, String message)
+ : base(message, parameterName)
+ {
+ HResult = __HResults.COR_E_DUPLICATEWAITOBJECT;
+ }
+
+ public DuplicateWaitObjectException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_DUPLICATEWAITOBJECT;
+ }
+
+ // This constructor is required for serialization
+ protected DuplicateWaitObjectException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/EntryPointNotFoundException.cs b/src/mscorlib/shared/System/EntryPointNotFoundException.cs
new file mode 100644
index 0000000000..835d33413d
--- /dev/null
+++ b/src/mscorlib/shared/System/EntryPointNotFoundException.cs
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: The exception class for some failed P/Invoke calls.
+**
+**
+=============================================================================*/
+
+using System;
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class EntryPointNotFoundException : TypeLoadException
+ {
+ public EntryPointNotFoundException()
+ : base(SR.Arg_EntryPointNotFoundException)
+ {
+ HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND;
+ }
+
+ public EntryPointNotFoundException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND;
+ }
+
+ public EntryPointNotFoundException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_ENTRYPOINTNOTFOUND;
+ }
+
+ protected EntryPointNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/EventArgs.cs b/src/mscorlib/shared/System/EventArgs.cs
new file mode 100644
index 0000000000..c0356613de
--- /dev/null
+++ b/src/mscorlib/shared/System/EventArgs.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System
+{
+ // The base class for all event classes.
+ [Serializable]
+ public class EventArgs
+ {
+ public static readonly EventArgs Empty = new EventArgs();
+
+ public EventArgs()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/EventHandler.cs b/src/mscorlib/shared/System/EventHandler.cs
new file mode 100644
index 0000000000..e6923cf637
--- /dev/null
+++ b/src/mscorlib/shared/System/EventHandler.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System
+{
+ [Serializable]
+ public delegate void EventHandler(Object sender, EventArgs e);
+
+ [Serializable]
+ public delegate void EventHandler<TEventArgs>(Object sender, TEventArgs e); // Removed TEventArgs constraint post-.NET 4
+}
diff --git a/src/mscorlib/shared/System/ExecutionEngineException.cs b/src/mscorlib/shared/System/ExecutionEngineException.cs
new file mode 100644
index 0000000000..bebfd493a0
--- /dev/null
+++ b/src/mscorlib/shared/System/ExecutionEngineException.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+//
+/*=============================================================================
+**
+**
+**
+** Purpose: The exception class for misc execution engine exceptions.
+** Currently, its only used as a placeholder type when the EE
+** does a FailFast.
+**
+**
+=============================================================================*/
+
+using System;
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Obsolete("This type previously indicated an unspecified fatal error in the runtime. The runtime no longer raises this exception so this type is obsolete.")]
+ [Serializable]
+ public sealed class ExecutionEngineException : SystemException
+ {
+ public ExecutionEngineException()
+ : base(SR.Arg_ExecutionEngineException)
+ {
+ HResult = __HResults.COR_E_EXECUTIONENGINE;
+ }
+
+ public ExecutionEngineException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_EXECUTIONENGINE;
+ }
+
+ public ExecutionEngineException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_EXECUTIONENGINE;
+ }
+
+ internal ExecutionEngineException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/FieldAccessException.cs b/src/mscorlib/shared/System/FieldAccessException.cs
new file mode 100644
index 0000000000..ac62c0fcac
--- /dev/null
+++ b/src/mscorlib/shared/System/FieldAccessException.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+** Purpose: The exception class for class loading failures.
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class FieldAccessException : MemberAccessException
+ {
+ public FieldAccessException()
+ : base(SR.Arg_FieldAccessException)
+ {
+ HResult = __HResults.COR_E_FIELDACCESS;
+ }
+
+ public FieldAccessException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_FIELDACCESS;
+ }
+
+ public FieldAccessException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_FIELDACCESS;
+ }
+
+ protected FieldAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/FlagsAttribute.cs b/src/mscorlib/shared/System/FlagsAttribute.cs
new file mode 100644
index 0000000000..5f8c108ae4
--- /dev/null
+++ b/src/mscorlib/shared/System/FlagsAttribute.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+namespace System
+{
+ // Custom attribute to indicate that the enum
+ // should be treated as a bitfield (or set of flags).
+ // An IDE may use this information to provide a richer
+ // development experience.
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Enum, Inherited = false)]
+ public class FlagsAttribute : Attribute
+ {
+ public FlagsAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/FormatException.cs b/src/mscorlib/shared/System/FormatException.cs
new file mode 100644
index 0000000000..c5758e11cd
--- /dev/null
+++ b/src/mscorlib/shared/System/FormatException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Exception to designate an illegal argument to FormatMessage.
+**
+**
+===========================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class FormatException : SystemException
+ {
+ public FormatException()
+ : base(SR.Arg_FormatException)
+ {
+ HResult = __HResults.COR_E_FORMAT;
+ }
+
+ public FormatException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_FORMAT;
+ }
+
+ public FormatException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_FORMAT;
+ }
+
+ protected FormatException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/FormattableString.cs b/src/mscorlib/shared/System/FormattableString.cs
new file mode 100644
index 0000000000..6369363b5d
--- /dev/null
+++ b/src/mscorlib/shared/System/FormattableString.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: implementation of the FormattableString
+** class.
+**
+===========================================================*/
+
+namespace System
+{
+ /// <summary>
+ /// A composite format string along with the arguments to be formatted. An instance of this
+ /// type may result from the use of the C# or VB language primitive "interpolated string".
+ /// </summary>
+ public abstract class FormattableString : IFormattable
+ {
+ /// <summary>
+ /// The composite format string.
+ /// </summary>
+ public abstract string Format { get; }
+
+ /// <summary>
+ /// Returns an object array that contains zero or more objects to format. Clients should not
+ /// mutate the contents of the array.
+ /// </summary>
+ public abstract object[] GetArguments();
+
+ /// <summary>
+ /// The number of arguments to be formatted.
+ /// </summary>
+ public abstract int ArgumentCount { get; }
+
+ /// <summary>
+ /// Returns one argument to be formatted from argument position <paramref name="index"/>.
+ /// </summary>
+ public abstract object GetArgument(int index);
+
+ /// <summary>
+ /// Format to a string using the given culture.
+ /// </summary>
+ public abstract string ToString(IFormatProvider formatProvider);
+
+ string IFormattable.ToString(string ignored, IFormatProvider formatProvider)
+ {
+ return ToString(formatProvider);
+ }
+
+ /// <summary>
+ /// Format the given object in the invariant culture. This static method may be
+ /// imported in C# by
+ /// <code>
+ /// using static System.FormattableString;
+ /// </code>.
+ /// Within the scope
+ /// of that import directive an interpolated string may be formatted in the
+ /// invariant culture by writing, for example,
+ /// <code>
+ /// Invariant($"{{ lat = {latitude}; lon = {longitude} }}")
+ /// </code>
+ /// </summary>
+ public static string Invariant(FormattableString formattable)
+ {
+ if (formattable == null)
+ {
+ throw new ArgumentNullException(nameof(formattable));
+ }
+
+ return formattable.ToString(Globalization.CultureInfo.InvariantCulture);
+ }
+
+ public override string ToString()
+ {
+ return ToString(Globalization.CultureInfo.CurrentCulture);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/CalendarAlgorithmType.cs b/src/mscorlib/shared/System/Globalization/CalendarAlgorithmType.cs
new file mode 100644
index 0000000000..4ddc307abf
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/CalendarAlgorithmType.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ public enum CalendarAlgorithmType
+ {
+ Unknown = 0, // This is the default value to return in the Calendar base class.
+ SolarCalendar = 1, // Solar-base calendar, such as GregorianCalendar, jaoaneseCalendar, JulianCalendar, etc.
+ // Solar calendars are based on the solar year and seasons.
+ LunarCalendar = 2, // Lunar-based calendar, such as Hijri and UmAlQuraCalendar.
+ // Lunar calendars are based on the path of the moon. The seasons are not accurately represented.
+ LunisolarCalendar = 3 // Lunisolar-based calendar which use leap month rule, such as HebrewCalendar and Asian Lunisolar calendars.
+ // Lunisolar calendars are based on the cycle of the moon, but consider the seasons as a secondary consideration,
+ // so they align with the seasons as well as lunar events.
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/CalendarWeekRule.cs b/src/mscorlib/shared/System/Globalization/CalendarWeekRule.cs
new file mode 100644
index 0000000000..b381b5c544
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/CalendarWeekRule.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ [Serializable]
+ public enum CalendarWeekRule
+ {
+ FirstDay = 0, // Week 1 begins on the first day of the year
+
+ FirstFullWeek = 1, // Week 1 begins on first FirstDayOfWeek not before the first day of the year
+
+ FirstFourDayWeek = 2 // Week 1 begins on first FirstDayOfWeek such that FirstDayOfWeek+3 is not before the first day of the year
+ };
+}
diff --git a/src/mscorlib/shared/System/Globalization/CalendricalCalculationsHelper.cs b/src/mscorlib/shared/System/Globalization/CalendricalCalculationsHelper.cs
new file mode 100644
index 0000000000..7de75d6aee
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/CalendricalCalculationsHelper.cs
@@ -0,0 +1,412 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+
+namespace System.Globalization
+{
+ internal class CalendricalCalculationsHelper
+ {
+ private const double FullCircleOfArc = 360.0; // 360.0;
+ private const int HalfCircleOfArc = 180;
+ private const double TwelveHours = 0.5; // half a day
+ private const double Noon2000Jan01 = 730120.5;
+ internal const double MeanTropicalYearInDays = 365.242189;
+ private const double MeanSpeedOfSun = MeanTropicalYearInDays / FullCircleOfArc;
+ private const double LongitudeSpring = 0.0;
+ private const double TwoDegreesAfterSpring = 2.0;
+ private const int SecondsPerDay = 24 * 60 * 60; // 24 hours * 60 minutes * 60 seconds
+
+ private const int DaysInUniformLengthCentury = 36525;
+ private const int SecondsPerMinute = 60;
+ private const int MinutesPerDegree = 60;
+
+ private static readonly long s_startOf1810 = GetNumberOfDays(new DateTime(1810, 1, 1));
+ private static readonly long s_startOf1900Century = GetNumberOfDays(new DateTime(1900, 1, 1));
+
+ private static readonly double[] s_coefficients1900to1987 = new double[] { -0.00002, 0.000297, 0.025184, -0.181133, 0.553040, -0.861938, 0.677066, -0.212591 };
+ private static readonly double[] s_coefficients1800to1899 = new double[] { -0.000009, 0.003844, 0.083563, 0.865736, 4.867575, 15.845535, 31.332267, 38.291999, 28.316289, 11.636204, 2.043794 };
+ private static readonly double[] s_coefficients1700to1799 = new double[] { 8.118780842, -0.005092142, 0.003336121, -0.0000266484 };
+ private static readonly double[] s_coefficients1620to1699 = new double[] { 196.58333, -4.0675, 0.0219167 };
+ private static readonly double[] s_lambdaCoefficients = new double[] { 280.46645, 36000.76983, 0.0003032 };
+ private static readonly double[] s_anomalyCoefficients = new double[] { 357.52910, 35999.05030, -0.0001559, -0.00000048 };
+ private static readonly double[] s_eccentricityCoefficients = new double[] { 0.016708617, -0.000042037, -0.0000001236 };
+ private static readonly double[] s_coefficients = new double[] { Angle(23, 26, 21.448), Angle(0, 0, -46.8150), Angle(0, 0, -0.00059), Angle(0, 0, 0.001813) };
+ private static readonly double[] s_coefficientsA = new double[] { 124.90, -1934.134, 0.002063 };
+ private static readonly double[] s_coefficientsB = new double[] { 201.11, 72001.5377, 0.00057 };
+
+ private static double RadiansFromDegrees(double degree)
+ {
+ return degree * Math.PI / 180;
+ }
+
+ private static double SinOfDegree(double degree)
+ {
+ return Math.Sin(RadiansFromDegrees(degree));
+ }
+
+ private static double CosOfDegree(double degree)
+ {
+ return Math.Cos(RadiansFromDegrees(degree));
+ }
+ private static double TanOfDegree(double degree)
+ {
+ return Math.Tan(RadiansFromDegrees(degree));
+ }
+
+ public static double Angle(int degrees, int minutes, double seconds)
+ {
+ return ((seconds / SecondsPerMinute + minutes) / MinutesPerDegree) + degrees;
+ }
+
+ private static double Obliquity(double julianCenturies)
+ {
+ return PolynomialSum(s_coefficients, julianCenturies);
+ }
+
+ internal static long GetNumberOfDays(DateTime date)
+ {
+ return date.Ticks / GregorianCalendar.TicksPerDay;
+ }
+
+ private static int GetGregorianYear(double numberOfDays)
+ {
+ return new DateTime(Math.Min((long)(Math.Floor(numberOfDays) * GregorianCalendar.TicksPerDay), DateTime.MaxValue.Ticks)).Year;
+ }
+
+ private enum CorrectionAlgorithm
+ {
+ Default,
+ Year1988to2019,
+ Year1900to1987,
+ Year1800to1899,
+ Year1700to1799,
+ Year1620to1699
+ }
+
+ private struct EphemerisCorrectionAlgorithmMap
+ {
+ public EphemerisCorrectionAlgorithmMap(int year, CorrectionAlgorithm algorithm)
+ {
+ _lowestYear = year;
+ _algorithm = algorithm;
+ }
+
+ internal int _lowestYear;
+ internal CorrectionAlgorithm _algorithm;
+ };
+
+ private static readonly EphemerisCorrectionAlgorithmMap[] s_ephemerisCorrectionTable = new EphemerisCorrectionAlgorithmMap[]
+ {
+ // lowest year that starts algorithm, algorithm to use
+ new EphemerisCorrectionAlgorithmMap(2020, CorrectionAlgorithm.Default),
+ new EphemerisCorrectionAlgorithmMap(1988, CorrectionAlgorithm.Year1988to2019),
+ new EphemerisCorrectionAlgorithmMap(1900, CorrectionAlgorithm.Year1900to1987),
+ new EphemerisCorrectionAlgorithmMap(1800, CorrectionAlgorithm.Year1800to1899),
+ new EphemerisCorrectionAlgorithmMap(1700, CorrectionAlgorithm.Year1700to1799),
+ new EphemerisCorrectionAlgorithmMap(1620, CorrectionAlgorithm.Year1620to1699),
+ new EphemerisCorrectionAlgorithmMap(int.MinValue, CorrectionAlgorithm.Default) // default must be last
+ };
+
+ private static double Reminder(double divisor, double dividend)
+ {
+ double whole = Math.Floor(divisor / dividend);
+ return divisor - (dividend * whole);
+ }
+
+ private static double NormalizeLongitude(double longitude)
+ {
+ longitude = Reminder(longitude, FullCircleOfArc);
+ if (longitude < 0)
+ {
+ longitude += FullCircleOfArc;
+ }
+ return longitude;
+ }
+
+ public static double AsDayFraction(double longitude)
+ {
+ return longitude / FullCircleOfArc;
+ }
+
+ private static double PolynomialSum(double[] coefficients, double indeterminate)
+ {
+ double sum = coefficients[0];
+ double indeterminateRaised = 1;
+ for (int i = 1; i < coefficients.Length; i++)
+ {
+ indeterminateRaised *= indeterminate;
+ sum += (coefficients[i] * indeterminateRaised);
+ }
+
+ return sum;
+ }
+
+ private static double CenturiesFrom1900(int gregorianYear)
+ {
+ long july1stOfYear = GetNumberOfDays(new DateTime(gregorianYear, 7, 1));
+ return (double)(july1stOfYear - s_startOf1900Century) / DaysInUniformLengthCentury;
+ }
+
+ // the following formulas defines a polynomial function which gives us the amount that the earth is slowing down for specific year ranges
+ private static double DefaultEphemerisCorrection(int gregorianYear)
+ {
+ Debug.Assert(gregorianYear < 1620 || 2020 <= gregorianYear);
+ long january1stOfYear = GetNumberOfDays(new DateTime(gregorianYear, 1, 1));
+ double daysSinceStartOf1810 = january1stOfYear - s_startOf1810;
+ double x = TwelveHours + daysSinceStartOf1810;
+ return ((Math.Pow(x, 2) / 41048480) - 15) / SecondsPerDay;
+ }
+
+ private static double EphemerisCorrection1988to2019(int gregorianYear)
+ {
+ Debug.Assert(1988 <= gregorianYear && gregorianYear <= 2019);
+ return (double)(gregorianYear - 1933) / SecondsPerDay;
+ }
+
+ private static double EphemerisCorrection1900to1987(int gregorianYear)
+ {
+ Debug.Assert(1900 <= gregorianYear && gregorianYear <= 1987);
+ double centuriesFrom1900 = CenturiesFrom1900(gregorianYear);
+ return PolynomialSum(s_coefficients1900to1987, centuriesFrom1900);
+ }
+
+ private static double EphemerisCorrection1800to1899(int gregorianYear)
+ {
+ Debug.Assert(1800 <= gregorianYear && gregorianYear <= 1899);
+ double centuriesFrom1900 = CenturiesFrom1900(gregorianYear);
+ return PolynomialSum(s_coefficients1800to1899, centuriesFrom1900);
+ }
+
+ private static double EphemerisCorrection1700to1799(int gregorianYear)
+ {
+ Debug.Assert(1700 <= gregorianYear && gregorianYear <= 1799);
+ double yearsSince1700 = gregorianYear - 1700;
+ return PolynomialSum(s_coefficients1700to1799, yearsSince1700) / SecondsPerDay;
+ }
+
+ private static double EphemerisCorrection1620to1699(int gregorianYear)
+ {
+ Debug.Assert(1620 <= gregorianYear && gregorianYear <= 1699);
+ double yearsSince1600 = gregorianYear - 1600;
+ return PolynomialSum(s_coefficients1620to1699, yearsSince1600) / SecondsPerDay;
+ }
+
+ // ephemeris-correction: correction to account for the slowing down of the rotation of the earth
+ private static double EphemerisCorrection(double time)
+ {
+ int year = GetGregorianYear(time);
+ foreach (EphemerisCorrectionAlgorithmMap map in s_ephemerisCorrectionTable)
+ {
+ if (map._lowestYear <= year)
+ {
+ switch (map._algorithm)
+ {
+ case CorrectionAlgorithm.Default: return DefaultEphemerisCorrection(year);
+ case CorrectionAlgorithm.Year1988to2019: return EphemerisCorrection1988to2019(year);
+ case CorrectionAlgorithm.Year1900to1987: return EphemerisCorrection1900to1987(year);
+ case CorrectionAlgorithm.Year1800to1899: return EphemerisCorrection1800to1899(year);
+ case CorrectionAlgorithm.Year1700to1799: return EphemerisCorrection1700to1799(year);
+ case CorrectionAlgorithm.Year1620to1699: return EphemerisCorrection1620to1699(year);
+ }
+
+ break; // break the loop and assert eventually
+ }
+ }
+
+ Debug.Assert(false, "Not expected to come here");
+ return DefaultEphemerisCorrection(year);
+ }
+
+ public static double JulianCenturies(double moment)
+ {
+ double dynamicalMoment = moment + EphemerisCorrection(moment);
+ return (dynamicalMoment - Noon2000Jan01) / DaysInUniformLengthCentury;
+ }
+
+ private static bool IsNegative(double value)
+ {
+ return Math.Sign(value) == -1;
+ }
+
+ private static double CopySign(double value, double sign)
+ {
+ return (IsNegative(value) == IsNegative(sign)) ? value : -value;
+ }
+
+ // equation-of-time; approximate the difference between apparent solar time and mean solar time
+ // formal definition is EOT = GHA - GMHA
+ // GHA is the Greenwich Hour Angle of the apparent (actual) Sun
+ // GMHA is the Greenwich Mean Hour Angle of the mean (fictitious) Sun
+ // http://www.esrl.noaa.gov/gmd/grad/solcalc/
+ // http://en.wikipedia.org/wiki/Equation_of_time
+ private static double EquationOfTime(double time)
+ {
+ double julianCenturies = JulianCenturies(time);
+ double lambda = PolynomialSum(s_lambdaCoefficients, julianCenturies);
+ double anomaly = PolynomialSum(s_anomalyCoefficients, julianCenturies);
+ double eccentricity = PolynomialSum(s_eccentricityCoefficients, julianCenturies);
+
+ double epsilon = Obliquity(julianCenturies);
+ double tanHalfEpsilon = TanOfDegree(epsilon / 2);
+ double y = tanHalfEpsilon * tanHalfEpsilon;
+
+ double dividend = ((y * SinOfDegree(2 * lambda))
+ - (2 * eccentricity * SinOfDegree(anomaly))
+ + (4 * eccentricity * y * SinOfDegree(anomaly) * CosOfDegree(2 * lambda))
+ - (0.5 * Math.Pow(y, 2) * SinOfDegree(4 * lambda))
+ - (1.25 * Math.Pow(eccentricity, 2) * SinOfDegree(2 * anomaly)));
+ double divisor = 2 * Math.PI;
+ double equation = dividend / divisor;
+
+ // approximation of equation of time is not valid for dates that are many millennia in the past or future
+ // thus limited to a half day
+ return CopySign(Math.Min(Math.Abs(equation), TwelveHours), equation);
+ }
+
+ private static double AsLocalTime(double apparentMidday, double longitude)
+ {
+ // slightly inaccurate since equation of time takes mean time not apparent time as its argument, but the difference is negligible
+ double universalTime = apparentMidday - AsDayFraction(longitude);
+ return apparentMidday - EquationOfTime(universalTime);
+ }
+
+ // midday
+ public static double Midday(double date, double longitude)
+ {
+ return AsLocalTime(date + TwelveHours, longitude) - AsDayFraction(longitude);
+ }
+
+ private static double InitLongitude(double longitude)
+ {
+ return NormalizeLongitude(longitude + HalfCircleOfArc) - HalfCircleOfArc;
+ }
+
+ // midday-in-tehran
+ public static double MiddayAtPersianObservationSite(double date)
+ {
+ return Midday(date, InitLongitude(52.5)); // 52.5 degrees east - longitude of UTC+3:30 which defines Iranian Standard Time
+ }
+
+ private static double PeriodicTerm(double julianCenturies, int x, double y, double z)
+ {
+ return x * SinOfDegree(y + z * julianCenturies);
+ }
+
+ private static double SumLongSequenceOfPeriodicTerms(double julianCenturies)
+ {
+ double sum = 0.0;
+ sum += PeriodicTerm(julianCenturies, 403406, 270.54861, 0.9287892);
+ sum += PeriodicTerm(julianCenturies, 195207, 340.19128, 35999.1376958);
+ sum += PeriodicTerm(julianCenturies, 119433, 63.91854, 35999.4089666);
+ sum += PeriodicTerm(julianCenturies, 112392, 331.2622, 35998.7287385);
+ sum += PeriodicTerm(julianCenturies, 3891, 317.843, 71998.20261);
+ sum += PeriodicTerm(julianCenturies, 2819, 86.631, 71998.4403);
+ sum += PeriodicTerm(julianCenturies, 1721, 240.052, 36000.35726);
+ sum += PeriodicTerm(julianCenturies, 660, 310.26, 71997.4812);
+ sum += PeriodicTerm(julianCenturies, 350, 247.23, 32964.4678);
+ sum += PeriodicTerm(julianCenturies, 334, 260.87, -19.441);
+ sum += PeriodicTerm(julianCenturies, 314, 297.82, 445267.1117);
+ sum += PeriodicTerm(julianCenturies, 268, 343.14, 45036.884);
+ sum += PeriodicTerm(julianCenturies, 242, 166.79, 3.1008);
+ sum += PeriodicTerm(julianCenturies, 234, 81.53, 22518.4434);
+ sum += PeriodicTerm(julianCenturies, 158, 3.5, -19.9739);
+ sum += PeriodicTerm(julianCenturies, 132, 132.75, 65928.9345);
+ sum += PeriodicTerm(julianCenturies, 129, 182.95, 9038.0293);
+ sum += PeriodicTerm(julianCenturies, 114, 162.03, 3034.7684);
+ sum += PeriodicTerm(julianCenturies, 99, 29.8, 33718.148);
+ sum += PeriodicTerm(julianCenturies, 93, 266.4, 3034.448);
+ sum += PeriodicTerm(julianCenturies, 86, 249.2, -2280.773);
+ sum += PeriodicTerm(julianCenturies, 78, 157.6, 29929.992);
+ sum += PeriodicTerm(julianCenturies, 72, 257.8, 31556.493);
+ sum += PeriodicTerm(julianCenturies, 68, 185.1, 149.588);
+ sum += PeriodicTerm(julianCenturies, 64, 69.9, 9037.75);
+ sum += PeriodicTerm(julianCenturies, 46, 8.0, 107997.405);
+ sum += PeriodicTerm(julianCenturies, 38, 197.1, -4444.176);
+ sum += PeriodicTerm(julianCenturies, 37, 250.4, 151.771);
+ sum += PeriodicTerm(julianCenturies, 32, 65.3, 67555.316);
+ sum += PeriodicTerm(julianCenturies, 29, 162.7, 31556.08);
+ sum += PeriodicTerm(julianCenturies, 28, 341.5, -4561.54);
+ sum += PeriodicTerm(julianCenturies, 27, 291.6, 107996.706);
+ sum += PeriodicTerm(julianCenturies, 27, 98.5, 1221.655);
+ sum += PeriodicTerm(julianCenturies, 25, 146.7, 62894.167);
+ sum += PeriodicTerm(julianCenturies, 24, 110.0, 31437.369);
+ sum += PeriodicTerm(julianCenturies, 21, 5.2, 14578.298);
+ sum += PeriodicTerm(julianCenturies, 21, 342.6, -31931.757);
+ sum += PeriodicTerm(julianCenturies, 20, 230.9, 34777.243);
+ sum += PeriodicTerm(julianCenturies, 18, 256.1, 1221.999);
+ sum += PeriodicTerm(julianCenturies, 17, 45.3, 62894.511);
+ sum += PeriodicTerm(julianCenturies, 14, 242.9, -4442.039);
+ sum += PeriodicTerm(julianCenturies, 13, 115.2, 107997.909);
+ sum += PeriodicTerm(julianCenturies, 13, 151.8, 119.066);
+ sum += PeriodicTerm(julianCenturies, 13, 285.3, 16859.071);
+ sum += PeriodicTerm(julianCenturies, 12, 53.3, -4.578);
+ sum += PeriodicTerm(julianCenturies, 10, 126.6, 26895.292);
+ sum += PeriodicTerm(julianCenturies, 10, 205.7, -39.127);
+ sum += PeriodicTerm(julianCenturies, 10, 85.9, 12297.536);
+ sum += PeriodicTerm(julianCenturies, 10, 146.1, 90073.778);
+ return sum;
+ }
+
+ private static double Aberration(double julianCenturies)
+ {
+ return (0.0000974 * CosOfDegree(177.63 + (35999.01848 * julianCenturies))) - 0.005575;
+ }
+
+ private static double Nutation(double julianCenturies)
+ {
+ double a = PolynomialSum(s_coefficientsA, julianCenturies);
+ double b = PolynomialSum(s_coefficientsB, julianCenturies);
+ return (-0.004778 * SinOfDegree(a)) - (0.0003667 * SinOfDegree(b));
+ }
+
+ public static double Compute(double time)
+ {
+ double julianCenturies = JulianCenturies(time);
+ double lambda = 282.7771834
+ + (36000.76953744 * julianCenturies)
+ + (0.000005729577951308232 * SumLongSequenceOfPeriodicTerms(julianCenturies));
+
+ double longitude = lambda + Aberration(julianCenturies) + Nutation(julianCenturies);
+ return InitLongitude(longitude);
+ }
+
+ public static double AsSeason(double longitude)
+ {
+ return (longitude < 0) ? (longitude + FullCircleOfArc) : longitude;
+ }
+
+ private static double EstimatePrior(double longitude, double time)
+ {
+ double timeSunLastAtLongitude = time - (MeanSpeedOfSun * AsSeason(InitLongitude(Compute(time) - longitude)));
+ double longitudeErrorDelta = InitLongitude(Compute(timeSunLastAtLongitude) - longitude);
+ return Math.Min(time, timeSunLastAtLongitude - (MeanSpeedOfSun * longitudeErrorDelta));
+ }
+
+ // persian-new-year-on-or-before
+ // number of days is the absolute date. The absolute date is the number of days from January 1st, 1 A.D.
+ // 1/1/0001 is absolute date 1.
+ internal static long PersianNewYearOnOrBefore(long numberOfDays)
+ {
+ double date = (double)numberOfDays;
+
+ double approx = EstimatePrior(LongitudeSpring, MiddayAtPersianObservationSite(date));
+ long lowerBoundNewYearDay = (long)Math.Floor(approx) - 1;
+ long upperBoundNewYearDay = lowerBoundNewYearDay + 3; // estimate is generally within a day of the actual occurrance (at the limits, the error expands, since the calculations rely on the mean tropical year which changes...)
+ long day = lowerBoundNewYearDay;
+ for (; day != upperBoundNewYearDay; ++day)
+ {
+ double midday = MiddayAtPersianObservationSite((double)day);
+ double l = Compute(midday);
+ if ((LongitudeSpring <= l) && (l <= TwoDegreesAfterSpring))
+ {
+ break;
+ }
+ }
+ Debug.Assert(day != upperBoundNewYearDay);
+
+ return day - 1;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/ChineseLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/ChineseLunisolarCalendar.cs
new file mode 100644
index 0000000000..404add0936
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/ChineseLunisolarCalendar.cs
@@ -0,0 +1,390 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ /*
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 1901/02/19 2101/01/28
+ ** ChineseLunisolar 1901/01/01 2100/12/29
+ */
+
+ [Serializable]
+ public class ChineseLunisolarCalendar : EastAsianLunisolarCalendar
+ {
+ //
+ // The era value for the current era.
+ //
+
+ public const int ChineseEra = 1;
+
+ internal const int MIN_LUNISOLAR_YEAR = 1901;
+ internal const int MAX_LUNISOLAR_YEAR = 2100;
+
+ internal const int MIN_GREGORIAN_YEAR = 1901;
+ internal const int MIN_GREGORIAN_MONTH = 2;
+ internal const int MIN_GREGORIAN_DAY = 19;
+
+ internal const int MAX_GREGORIAN_YEAR = 2101;
+ internal const int MAX_GREGORIAN_MONTH = 1;
+ internal const int MAX_GREGORIAN_DAY = 28;
+
+ internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY);
+ internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999);
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+
+ protected override int DaysInYearBeforeMinSupportedYear
+ {
+ get
+ {
+ // 1900: 1-29 2-30 3-29 4-29 5-30 6-29 7-30 8-30 Leap8-29 9-30 10-30 11-29 12-30 from Calendrical Tabulations
+ return 384;
+ }
+ }
+
+
+ private static readonly int[,] s_yinfo =
+ {
+ /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days
+ 1901 */
+ { 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1902 */{ 0 , 2 , 8 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1903 */{ 5 , 1 , 29 , 21096 },/* 29 30 29 30 29 29 30 29 29 30 30 29 30 383
+1904 */{ 0 , 2 , 16 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+1905 */{ 0 , 2 , 4 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355
+1906 */{ 4 , 1 , 25 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384
+1907 */{ 0 , 2 , 13 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1908 */{ 0 , 2 , 2 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355
+1909 */{ 2 , 1 , 22 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1910 */{ 0 , 2 , 10 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1911 */{ 6 , 1 , 30 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+1912 */{ 0 , 2 , 18 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1913 */{ 0 , 2 , 6 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1914 */{ 5 , 1 , 26 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384
+1915 */{ 0 , 2 , 14 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354
+1916 */{ 0 , 2 , 3 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355
+1917 */{ 2 , 1 , 23 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384
+1918 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1919 */{ 7 , 2 , 1 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384
+1920 */{ 0 , 2 , 20 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1921 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1922 */{ 5 , 1 , 28 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1923 */{ 0 , 2 , 16 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1924 */{ 0 , 2 , 5 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+1925 */{ 4 , 1 , 24 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385
+1926 */{ 0 , 2 , 13 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1927 */{ 0 , 2 , 2 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355
+1928 */{ 2 , 1 , 23 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384
+1929 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1930 */{ 6 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383
+1931 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1932 */{ 0 , 2 , 6 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355
+1933 */{ 5 , 1 , 26 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384
+1934 */{ 0 , 2 , 14 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355
+1935 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1936 */{ 3 , 1 , 24 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384
+1937 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1938 */{ 7 , 1 , 31 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384
+1939 */{ 0 , 2 , 19 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354
+1940 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1941 */{ 6 , 1 , 27 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384
+1942 */{ 0 , 2 , 15 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1943 */{ 0 , 2 , 5 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1944 */{ 4 , 1 , 25 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385
+1945 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1946 */{ 0 , 2 , 2 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354
+1947 */{ 2 , 1 , 22 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384
+1948 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1949 */{ 7 , 1 , 29 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384
+1950 */{ 0 , 2 , 17 , 27808 },/* 29 30 30 29 30 30 29 29 30 29 30 29 0 354
+1951 */{ 0 , 2 , 6 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1952 */{ 5 , 1 , 27 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384
+1953 */{ 0 , 2 , 14 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354
+1954 */{ 0 , 2 , 3 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+1955 */{ 3 , 1 , 24 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+1956 */{ 0 , 2 , 12 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1957 */{ 8 , 1 , 31 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383
+1958 */{ 0 , 2 , 18 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355
+1959 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1960 */{ 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1962 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1965 */{ 0 , 2 , 2 , 21088 },/* 29 30 29 30 29 29 30 29 29 30 30 29 0 353
+1966 */{ 3 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355
+1968 */{ 7 , 1 , 30 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1970 */{ 0 , 2 , 6 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1972 */{ 0 , 2 , 15 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354
+1973 */{ 0 , 2 , 3 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1976 */{ 8 , 1 , 31 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384
+1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354
+1978 */{ 0 , 2 , 7 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355
+1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384
+1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1982 */{ 4 , 1 , 25 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+1987 */{ 6 , 1 , 29 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 29 384
+1988 */{ 0 , 2 , 17 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1989 */{ 0 , 2 , 6 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355
+1990 */{ 5 , 1 , 27 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384
+1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355
+1995 */{ 8 , 1 , 31 , 27432 },/* 29 30 30 29 30 29 30 30 29 29 30 29 30 384
+1996 */{ 0 , 2 , 19 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354
+1997 */{ 0 , 2 , 7 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1998 */{ 5 , 1 , 28 , 37736 },/* 30 29 29 30 29 29 30 30 29 30 30 29 30 384
+1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+2001 */{ 4 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+2005 */{ 0 , 2 , 9 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385
+2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354
+2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384
+2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+2012 */{ 4 , 1 , 23 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+2013 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384
+2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354
+2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+2017 */{ 6 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+2019 */{ 0 , 2 , 5 , 43312 },/* 30 29 30 29 30 29 29 30 29 29 30 30 0 354
+2020 */{ 4 , 1 , 25 , 29864 },/* 29 30 30 30 29 30 29 29 30 29 30 29 30 384
+2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+2023 */{ 2 , 1 , 22 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384
+2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+2026 */{ 0 , 2 , 17 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354
+2027 */{ 0 , 2 , 6 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+2028 */{ 5 , 1 , 26 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+2029 */{ 0 , 2 , 13 , 54576 },/* 30 30 29 30 29 30 29 30 29 29 30 30 0 355
+2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+2031 */{ 3 , 1 , 23 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384
+2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+2034 */{ 0 , 2 , 19 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354
+2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+2036 */{ 6 , 1 , 28 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384
+2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354
+2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384
+2040 */{ 0 , 2 , 12 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355
+2041 */{ 0 , 2 , 1 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384
+2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+2046 */{ 0 , 2 , 6 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+2048 */{ 0 , 2 , 14 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354
+2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355
+2050 */{ 3 , 1 , 23 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384
+2051 */{ 0 , 2 , 11 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355
+2052 */{ 8 , 2 , 1 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384
+2053 */{ 0 , 2 , 19 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+2054 */{ 0 , 2 , 8 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+2055 */{ 6 , 1 , 28 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+2056 */{ 0 , 2 , 15 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355
+2057 */{ 0 , 2 , 4 , 27424 },/* 29 30 30 29 30 29 30 30 29 29 30 29 0 354
+2058 */{ 4 , 1 , 24 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384
+2059 */{ 0 , 2 , 12 , 43744 },/* 30 29 30 29 30 29 30 29 30 30 30 29 0 355
+2060 */{ 0 , 2 , 2 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+2061 */{ 3 , 1 , 21 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384
+2062 */{ 0 , 2 , 9 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+2063 */{ 7 , 1 , 29 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+2064 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+2065 */{ 0 , 2 , 5 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+2066 */{ 5 , 1 , 26 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+2067 */{ 0 , 2 , 14 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+2068 */{ 0 , 2 , 3 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355
+2069 */{ 4 , 1 , 23 , 21224 },/* 29 30 29 30 29 29 30 29 30 30 30 29 30 384
+2070 */{ 0 , 2 , 11 , 21200 },/* 29 30 29 30 29 29 30 29 30 30 29 30 0 354
+2071 */{ 8 , 1 , 31 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+2072 */{ 0 , 2 , 19 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+2073 */{ 0 , 2 , 7 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+2074 */{ 6 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+2075 */{ 0 , 2 , 15 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+2076 */{ 0 , 2 , 5 , 21920 },/* 29 30 29 30 29 30 29 30 30 29 30 29 0 354
+2077 */{ 4 , 1 , 24 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384
+2078 */{ 0 , 2 , 12 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+2079 */{ 0 , 2 , 2 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+2080 */{ 3 , 1 , 22 , 43320 },/* 30 29 30 29 30 29 29 30 29 29 30 30 30 384
+2081 */{ 0 , 2 , 9 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354
+2082 */{ 7 , 1 , 29 , 29336 },/* 29 30 30 30 29 29 30 29 30 29 29 30 30 384
+2083 */{ 0 , 2 , 17 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+2084 */{ 0 , 2 , 6 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+2085 */{ 5 , 1 , 26 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384
+2086 */{ 0 , 2 , 14 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+2087 */{ 0 , 2 , 3 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+2088 */{ 4 , 1 , 24 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383
+2089 */{ 0 , 2 , 10 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+2090 */{ 8 , 1 , 30 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+2091 */{ 0 , 2 , 18 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354
+2092 */{ 0 , 2 , 7 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355
+2093 */{ 6 , 1 , 27 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384
+2094 */{ 0 , 2 , 15 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+2095 */{ 0 , 2 , 5 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+2096 */{ 4 , 1 , 25 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384
+2097 */{ 0 , 2 , 12 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+2098 */{ 0 , 2 , 1 , 53584 },/* 30 30 29 30 29 29 29 30 29 30 29 30 0 354
+2099 */{ 2 , 1 , 21 , 55592 },/* 30 30 29 30 30 29 29 30 29 29 30 29 30 384
+2100 */{ 0 , 2 , 9 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354
+ */};
+
+
+ internal override int MinCalendarYear
+ {
+ get
+ {
+ return (MIN_LUNISOLAR_YEAR);
+ }
+ }
+
+ internal override int MaxCalendarYear
+ {
+ get
+ {
+ return (MAX_LUNISOLAR_YEAR);
+ }
+ }
+
+ internal override DateTime MinDate
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+ internal override DateTime MaxDate
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+
+ internal override EraInfo[] CalEraInfo
+ {
+ get
+ {
+ return (null);
+ }
+ }
+
+ internal override int GetYearInfo(int lunarYear, int index)
+ {
+ if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR))
+ {
+ throw new ArgumentOutOfRangeException(
+ "year",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR));
+ }
+ Contract.EndContractBlock();
+
+ return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index];
+ }
+
+ internal override int GetYear(int year, DateTime time)
+ {
+ return year;
+ }
+
+ internal override int GetGregorianYear(int year, int era)
+ {
+ if (era != CurrentEra && era != ChineseEra)
+ {
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+
+ if (year < MIN_LUNISOLAR_YEAR || year > MAX_LUNISOLAR_YEAR)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR));
+ }
+ Contract.EndContractBlock();
+
+ return year;
+ }
+
+ public ChineseLunisolarCalendar()
+ {
+ }
+
+ public override int GetEra(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+ return (ChineseEra);
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return (CalendarId.CHINESELUNISOLAR);
+ }
+ }
+
+ internal override CalendarId BaseCalendarID
+ {
+ get
+ {
+ //Use CAL_GREGORIAN just to get CurrentEraValue as 1 since we do not have data under the ID CAL_ChineseLunisolar yet
+ return (CalendarId.GREGORIAN);
+ }
+ }
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (new int[] { ChineseEra });
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/CultureNotFoundException.cs b/src/mscorlib/shared/System/Globalization/CultureNotFoundException.cs
new file mode 100644
index 0000000000..929f4bb000
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/CultureNotFoundException.cs
@@ -0,0 +1,120 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Globalization
+{
+ [Serializable]
+ public class CultureNotFoundException : ArgumentException
+ {
+ private string _invalidCultureName; // unrecognized culture name
+ private int? _invalidCultureId; // unrecognized culture Lcid
+
+ public CultureNotFoundException()
+ : base(DefaultMessage)
+ {
+ }
+
+ public CultureNotFoundException(String message)
+ : base(message)
+ {
+ }
+
+ public CultureNotFoundException(String paramName, String message)
+ : base(message, paramName)
+ {
+ }
+
+ public CultureNotFoundException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public CultureNotFoundException(String paramName, string invalidCultureName, String message)
+ : base(message, paramName)
+ {
+ _invalidCultureName = invalidCultureName;
+ }
+
+ public CultureNotFoundException(String message, string invalidCultureName, Exception innerException)
+ : base(message, innerException)
+ {
+ _invalidCultureName = invalidCultureName;
+ }
+
+ public CultureNotFoundException(string message, int invalidCultureId, Exception innerException)
+ : base(message, innerException)
+ {
+ _invalidCultureId = invalidCultureId;
+ }
+
+ public CultureNotFoundException(string paramName, int invalidCultureId, string message)
+ : base(message, paramName)
+ {
+ _invalidCultureId = invalidCultureId;
+ }
+
+ protected CultureNotFoundException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ _invalidCultureId = (int?)info.GetValue("InvalidCultureId", typeof(int?));
+ _invalidCultureName = (string)info.GetValue("InvalidCultureName", typeof(string));
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData(info, context);
+ info.AddValue("InvalidCultureId", _invalidCultureId, typeof(int?));
+ info.AddValue("InvalidCultureName", _invalidCultureName, typeof(string));
+ }
+
+ public virtual Nullable<int> InvalidCultureId
+ {
+ get { return _invalidCultureId; }
+ }
+
+ public virtual string InvalidCultureName
+ {
+ get { return _invalidCultureName; }
+ }
+
+ private static String DefaultMessage
+ {
+ get
+ {
+ return SR.Argument_CultureNotSupported;
+ }
+ }
+
+ private String FormatedInvalidCultureId
+ {
+ get
+ {
+ return InvalidCultureId != null ?
+ String.Format(CultureInfo.InvariantCulture, "{0} (0x{0:x4})", (int)InvalidCultureId) :
+ InvalidCultureName;
+ }
+ }
+
+ public override String Message
+ {
+ get
+ {
+ String s = base.Message;
+ if (_invalidCultureId != null || _invalidCultureName != null)
+ {
+ String valueMessage = SR.Format(SR.Argument_CultureInvalidIdentifier, FormatedInvalidCultureId);
+ if (s == null)
+ {
+ return valueMessage;
+ }
+
+ return s + Environment.NewLine + valueMessage;
+ }
+ return s;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/CultureTypes.cs b/src/mscorlib/shared/System/Globalization/CultureTypes.cs
new file mode 100644
index 0000000000..f52ac82a83
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/CultureTypes.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// The enumeration constants used in CultureInfo.GetCultures().
+// On Linux platforms, the only enum values used there is NeutralCultures and SpecificCultures
+// the rest are obsolete or not valid on Linux
+
+namespace System.Globalization
+{
+ [Flags]
+ public enum CultureTypes
+ {
+ NeutralCultures = 0x0001, // Neutral cultures are cultures like "en", "de", "zh", etc, for enumeration this includes ALL neutrals regardless of other flags
+ SpecificCultures = 0x0002, // Non-netural cultuers. Examples are "en-us", "zh-tw", etc., for enumeration this includes ALL specifics regardless of other flags
+ InstalledWin32Cultures = 0x0004, // Win32 installed cultures in the system and exists in the framework too., this is effectively all cultures
+
+ AllCultures = NeutralCultures | SpecificCultures | InstalledWin32Cultures,
+
+ UserCustomCulture = 0x0008, // User defined custom culture
+ ReplacementCultures = 0x0010, // User defined replacement custom culture.
+ [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")]
+ WindowsOnlyCultures = 0x0020, // this will always return empty list.
+ [Obsolete("This value has been deprecated. Please use other values in CultureTypes.")]
+ FrameworkCultures = 0x0040, // will return only the v2 cultures marked as Framework culture.
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs b/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs
new file mode 100644
index 0000000000..840409f55a
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/DateTimeFormat.cs
@@ -0,0 +1,1206 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Text;
+
+namespace System
+{
+ /*
+ Customized format patterns:
+ P.S. Format in the table below is the internal number format used to display the pattern.
+
+ Patterns Format Description Example
+ ========= ========== ===================================== ========
+ "h" "0" hour (12-hour clock)w/o leading zero 3
+ "hh" "00" hour (12-hour clock)with leading zero 03
+ "hh*" "00" hour (12-hour clock)with leading zero 03
+
+ "H" "0" hour (24-hour clock)w/o leading zero 8
+ "HH" "00" hour (24-hour clock)with leading zero 08
+ "HH*" "00" hour (24-hour clock) 08
+
+ "m" "0" minute w/o leading zero
+ "mm" "00" minute with leading zero
+ "mm*" "00" minute with leading zero
+
+ "s" "0" second w/o leading zero
+ "ss" "00" second with leading zero
+ "ss*" "00" second with leading zero
+
+ "f" "0" second fraction (1 digit)
+ "ff" "00" second fraction (2 digit)
+ "fff" "000" second fraction (3 digit)
+ "ffff" "0000" second fraction (4 digit)
+ "fffff" "00000" second fraction (5 digit)
+ "ffffff" "000000" second fraction (6 digit)
+ "fffffff" "0000000" second fraction (7 digit)
+
+ "F" "0" second fraction (up to 1 digit)
+ "FF" "00" second fraction (up to 2 digit)
+ "FFF" "000" second fraction (up to 3 digit)
+ "FFFF" "0000" second fraction (up to 4 digit)
+ "FFFFF" "00000" second fraction (up to 5 digit)
+ "FFFFFF" "000000" second fraction (up to 6 digit)
+ "FFFFFFF" "0000000" second fraction (up to 7 digit)
+
+ "t" first character of AM/PM designator A
+ "tt" AM/PM designator AM
+ "tt*" AM/PM designator PM
+
+ "d" "0" day w/o leading zero 1
+ "dd" "00" day with leading zero 01
+ "ddd" short weekday name (abbreviation) Mon
+ "dddd" full weekday name Monday
+ "dddd*" full weekday name Monday
+
+
+ "M" "0" month w/o leading zero 2
+ "MM" "00" month with leading zero 02
+ "MMM" short month name (abbreviation) Feb
+ "MMMM" full month name Febuary
+ "MMMM*" full month name Febuary
+
+ "y" "0" two digit year (year % 100) w/o leading zero 0
+ "yy" "00" two digit year (year % 100) with leading zero 00
+ "yyy" "D3" year 2000
+ "yyyy" "D4" year 2000
+ "yyyyy" "D5" year 2000
+ ...
+
+ "z" "+0;-0" timezone offset w/o leading zero -8
+ "zz" "+00;-00" timezone offset with leading zero -08
+ "zzz" "+00;-00" for hour offset, "00" for minute offset full timezone offset -07:30
+ "zzz*" "+00;-00" for hour offset, "00" for minute offset full timezone offset -08:00
+
+ "K" -Local "zzz", e.g. -08:00
+ -Utc "'Z'", representing UTC
+ -Unspecified ""
+ -DateTimeOffset "zzzzz" e.g -07:30:15
+
+ "g*" the current era name A.D.
+
+ ":" time separator : -- DEPRECATED - Insert separator directly into pattern (eg: "H.mm.ss")
+ "/" date separator /-- DEPRECATED - Insert separator directly into pattern (eg: "M-dd-yyyy")
+ "'" quoted string 'ABC' will insert ABC into the formatted string.
+ '"' quoted string "ABC" will insert ABC into the formatted string.
+ "%" used to quote a single pattern characters E.g.The format character "%y" is to print two digit year.
+ "\" escaped character E.g. '\d' insert the character 'd' into the format string.
+ other characters insert the character into the format string.
+
+ Pre-defined format characters:
+ (U) to indicate Universal time is used.
+ (G) to indicate Gregorian calendar is used.
+
+ Format Description Real format Example
+ ========= ================================= ====================== =======================
+ "d" short date culture-specific 10/31/1999
+ "D" long data culture-specific Sunday, October 31, 1999
+ "f" full date (long date + short time) culture-specific Sunday, October 31, 1999 2:00 AM
+ "F" full date (long date + long time) culture-specific Sunday, October 31, 1999 2:00:00 AM
+ "g" general date (short date + short time) culture-specific 10/31/1999 2:00 AM
+ "G" general date (short date + long time) culture-specific 10/31/1999 2:00:00 AM
+ "m"/"M" Month/Day date culture-specific October 31
+(G) "o"/"O" Round Trip XML "yyyy-MM-ddTHH:mm:ss.fffffffK" 1999-10-31 02:00:00.0000000Z
+(G) "r"/"R" RFC 1123 date, "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'" Sun, 31 Oct 1999 10:00:00 GMT
+(G) "s" Sortable format, based on ISO 8601. "yyyy-MM-dd'T'HH:mm:ss" 1999-10-31T02:00:00
+ ('T' for local time)
+ "t" short time culture-specific 2:00 AM
+ "T" long time culture-specific 2:00:00 AM
+(G) "u" Universal time with sortable format, "yyyy'-'MM'-'dd HH':'mm':'ss'Z'" 1999-10-31 10:00:00Z
+ based on ISO 8601.
+(U) "U" Universal time with full culture-specific Sunday, October 31, 1999 10:00:00 AM
+ (long date + long time) format
+ "y"/"Y" Year/Month day culture-specific October, 1999
+
+ */
+
+ //This class contains only static members and does not require the serializable attribute.
+ internal static
+ class DateTimeFormat
+ {
+ internal const int MaxSecondsFractionDigits = 7;
+ internal static readonly TimeSpan NullOffset = TimeSpan.MinValue;
+
+ internal static char[] allStandardFormats =
+ {
+ 'd', 'D', 'f', 'F', 'g', 'G',
+ 'm', 'M', 'o', 'O', 'r', 'R',
+ 's', 't', 'T', 'u', 'U', 'y', 'Y',
+ };
+
+ internal const String RoundtripFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK";
+ internal const String RoundtripDateTimeUnfixed = "yyyy'-'MM'-'ddTHH':'mm':'ss zzz";
+
+ private const int DEFAULT_ALL_DATETIMES_SIZE = 132;
+
+ internal static readonly DateTimeFormatInfo InvariantFormatInfo = CultureInfo.InvariantCulture.DateTimeFormat;
+ internal static readonly string[] InvariantAbbreviatedMonthNames = InvariantFormatInfo.AbbreviatedMonthNames;
+ internal static readonly string[] InvariantAbbreviatedDayNames = InvariantFormatInfo.AbbreviatedDayNames;
+ internal const string Gmt = "GMT";
+
+ internal static String[] fixedNumberFormats = new String[] {
+ "0",
+ "00",
+ "000",
+ "0000",
+ "00000",
+ "000000",
+ "0000000",
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Format the positive integer value to a string and perfix with assigned
+ // length of leading zero.
+ //
+ // Parameters:
+ // value: The value to format
+ // len: The maximum length for leading zero.
+ // If the digits of the value is greater than len, no leading zero is added.
+ //
+ // Notes:
+ // The function can format to Int32.MaxValue.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal static void FormatDigits(StringBuilder outputBuffer, int value, int len)
+ {
+ Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0");
+ FormatDigits(outputBuffer, value, len, false);
+ }
+
+ internal unsafe static void FormatDigits(StringBuilder outputBuffer, int value, int len, bool overrideLengthLimit)
+ {
+ Debug.Assert(value >= 0, "DateTimeFormat.FormatDigits(): value >= 0");
+
+ // Limit the use of this function to be two-digits, so that we have the same behavior
+ // as RTM bits.
+ if (!overrideLengthLimit && len > 2)
+ {
+ len = 2;
+ }
+
+ char* buffer = stackalloc char[16];
+ char* p = buffer + 16;
+ int n = value;
+ do
+ {
+ *--p = (char)(n % 10 + '0');
+ n /= 10;
+ } while ((n != 0) && (p > buffer));
+
+ int digits = (int)(buffer + 16 - p);
+
+ //If the repeat count is greater than 0, we're trying
+ //to emulate the "00" format, so we have to prepend
+ //a zero if the string only has one character.
+ while ((digits < len) && (p > buffer))
+ {
+ *--p = '0';
+ digits++;
+ }
+ outputBuffer.Append(p, digits);
+ }
+
+ private static void HebrewFormatDigits(StringBuilder outputBuffer, int digits)
+ {
+ outputBuffer.Append(HebrewNumber.ToString(digits));
+ }
+
+ internal static int ParseRepeatPattern(String format, int pos, char patternChar)
+ {
+ int len = format.Length;
+ int index = pos + 1;
+ while ((index < len) && (format[index] == patternChar))
+ {
+ index++;
+ }
+ return (index - pos);
+ }
+
+ private static String FormatDayOfWeek(int dayOfWeek, int repeat, DateTimeFormatInfo dtfi)
+ {
+ Debug.Assert(dayOfWeek >= 0 && dayOfWeek <= 6, "dayOfWeek >= 0 && dayOfWeek <= 6");
+ if (repeat == 3)
+ {
+ return (dtfi.GetAbbreviatedDayName((DayOfWeek)dayOfWeek));
+ }
+ // Call dtfi.GetDayName() here, instead of accessing DayNames property, because we don't
+ // want a clone of DayNames, which will hurt perf.
+ return (dtfi.GetDayName((DayOfWeek)dayOfWeek));
+ }
+
+ private static String FormatMonth(int month, int repeatCount, DateTimeFormatInfo dtfi)
+ {
+ Debug.Assert(month >= 1 && month <= 12, "month >=1 && month <= 12");
+ if (repeatCount == 3)
+ {
+ return (dtfi.GetAbbreviatedMonthName(month));
+ }
+ // Call GetMonthName() here, instead of accessing MonthNames property, because we don't
+ // want a clone of MonthNames, which will hurt perf.
+ return (dtfi.GetMonthName(month));
+ }
+
+ //
+ // FormatHebrewMonthName
+ //
+ // Action: Return the Hebrew month name for the specified DateTime.
+ // Returns: The month name string for the specified DateTime.
+ // Arguments:
+ // time the time to format
+ // month The month is the value of HebrewCalendar.GetMonth(time).
+ // repeat Return abbreviated month name if repeat=3, or full month name if repeat=4
+ // dtfi The DateTimeFormatInfo which uses the Hebrew calendars as its calendar.
+ // Exceptions: None.
+ //
+
+ /* Note:
+ If DTFI is using Hebrew calendar, GetMonthName()/GetAbbreviatedMonthName() will return month names like this:
+ 1 Hebrew 1st Month
+ 2 Hebrew 2nd Month
+ .. ...
+ 6 Hebrew 6th Month
+ 7 Hebrew 6th Month II (used only in a leap year)
+ 8 Hebrew 7th Month
+ 9 Hebrew 8th Month
+ 10 Hebrew 9th Month
+ 11 Hebrew 10th Month
+ 12 Hebrew 11th Month
+ 13 Hebrew 12th Month
+
+ Therefore, if we are in a regular year, we have to increment the month name if moth is greater or eqaul to 7.
+ */
+ private static String FormatHebrewMonthName(DateTime time, int month, int repeatCount, DateTimeFormatInfo dtfi)
+ {
+ Debug.Assert(repeatCount != 3 || repeatCount != 4, "repeateCount should be 3 or 4");
+ if (dtfi.Calendar.IsLeapYear(dtfi.Calendar.GetYear(time)))
+ {
+ // This month is in a leap year
+ return (dtfi.internalGetMonthName(month, MonthNameStyles.LeapYear, (repeatCount == 3)));
+ }
+ // This is in a regular year.
+ if (month >= 7)
+ {
+ month++;
+ }
+ if (repeatCount == 3)
+ {
+ return (dtfi.GetAbbreviatedMonthName(month));
+ }
+ return (dtfi.GetMonthName(month));
+ }
+
+ //
+ // The pos should point to a quote character. This method will
+ // append to the result StringBuilder the string encloed by the quote character.
+ //
+ internal static int ParseQuoteString(String format, int pos, StringBuilder result)
+ {
+ //
+ // NOTE : pos will be the index of the quote character in the 'format' string.
+ //
+ int formatLen = format.Length;
+ int beginPos = pos;
+ char quoteChar = format[pos++]; // Get the character used to quote the following string.
+
+ bool foundQuote = false;
+ while (pos < formatLen)
+ {
+ char ch = format[pos++];
+ if (ch == quoteChar)
+ {
+ foundQuote = true;
+ break;
+ }
+ else if (ch == '\\')
+ {
+ // The following are used to support escaped character.
+ // Escaped character is also supported in the quoted string.
+ // Therefore, someone can use a format like "'minute:' mm\"" to display:
+ // minute: 45"
+ // because the second double quote is escaped.
+ if (pos < formatLen)
+ {
+ result.Append(format[pos++]);
+ }
+ else
+ {
+ //
+ // This means that '\' is at the end of the formatting string.
+ //
+ throw new FormatException(SR.Format_InvalidString);
+ }
+ }
+ else
+ {
+ result.Append(ch);
+ }
+ }
+
+ if (!foundQuote)
+ {
+ // Here we can't find the matching quote.
+ throw new FormatException(
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.Format_BadQuote, quoteChar));
+ }
+
+ //
+ // Return the character count including the begin/end quote characters and enclosed string.
+ //
+ return (pos - beginPos);
+ }
+
+ //
+ // Get the next character at the index of 'pos' in the 'format' string.
+ // Return value of -1 means 'pos' is already at the end of the 'format' string.
+ // Otherwise, return value is the int value of the next character.
+ //
+ internal static int ParseNextChar(String format, int pos)
+ {
+ if (pos >= format.Length - 1)
+ {
+ return (-1);
+ }
+ return ((int)format[pos + 1]);
+ }
+
+ //
+ // IsUseGenitiveForm
+ //
+ // Actions: Check the format to see if we should use genitive month in the formatting.
+ // Starting at the position (index) in the (format) string, look back and look ahead to
+ // see if there is "d" or "dd". In the case like "d MMMM" or "MMMM dd", we can use
+ // genitive form. Genitive form is not used if there is more than two "d".
+ // Arguments:
+ // format The format string to be scanned.
+ // index Where we should start the scanning. This is generally where "M" starts.
+ // tokenLen The len of the current pattern character. This indicates how many "M" that we have.
+ // patternToMatch The pattern that we want to search. This generally uses "d"
+ //
+ private static bool IsUseGenitiveForm(String format, int index, int tokenLen, char patternToMatch)
+ {
+ int i;
+ int repeat = 0;
+ //
+ // Look back to see if we can find "d" or "ddd"
+ //
+
+ // Find first "d".
+ for (i = index - 1; i >= 0 && format[i] != patternToMatch; i--) { /*Do nothing here */ };
+
+ if (i >= 0)
+ {
+ // Find a "d", so look back to see how many "d" that we can find.
+ while (--i >= 0 && format[i] == patternToMatch)
+ {
+ repeat++;
+ }
+ //
+ // repeat == 0 means that we have one (patternToMatch)
+ // repeat == 1 means that we have two (patternToMatch)
+ //
+ if (repeat <= 1)
+ {
+ return (true);
+ }
+ // Note that we can't just stop here. We may find "ddd" while looking back, and we have to look
+ // ahead to see if there is "d" or "dd".
+ }
+
+ //
+ // If we can't find "d" or "dd" by looking back, try look ahead.
+ //
+
+ // Find first "d"
+ for (i = index + tokenLen; i < format.Length && format[i] != patternToMatch; i++) { /* Do nothing here */ };
+
+ if (i < format.Length)
+ {
+ repeat = 0;
+ // Find a "d", so contine the walk to see how may "d" that we can find.
+ while (++i < format.Length && format[i] == patternToMatch)
+ {
+ repeat++;
+ }
+ //
+ // repeat == 0 means that we have one (patternToMatch)
+ // repeat == 1 means that we have two (patternToMatch)
+ //
+ if (repeat <= 1)
+ {
+ return (true);
+ }
+ }
+ return (false);
+ }
+
+
+ //
+ // FormatCustomized
+ //
+ // Actions: Format the DateTime instance using the specified format.
+ //
+ private static String FormatCustomized(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset)
+ {
+ Calendar cal = dtfi.Calendar;
+ StringBuilder result = StringBuilderCache.Acquire();
+ // This is a flag to indicate if we are format the dates using Hebrew calendar.
+
+ bool isHebrewCalendar = (cal.ID == CalendarId.HEBREW);
+ // This is a flag to indicate if we are formating hour/minute/second only.
+ bool bTimeOnly = true;
+
+ int i = 0;
+ int tokenLen, hour12;
+
+ while (i < format.Length)
+ {
+ char ch = format[i];
+ int nextChar;
+ switch (ch)
+ {
+ case 'g':
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ result.Append(dtfi.GetEraName(cal.GetEra(dateTime)));
+ break;
+ case 'h':
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ hour12 = dateTime.Hour % 12;
+ if (hour12 == 0)
+ {
+ hour12 = 12;
+ }
+ FormatDigits(result, hour12, tokenLen);
+ break;
+ case 'H':
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ FormatDigits(result, dateTime.Hour, tokenLen);
+ break;
+ case 'm':
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ FormatDigits(result, dateTime.Minute, tokenLen);
+ break;
+ case 's':
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ FormatDigits(result, dateTime.Second, tokenLen);
+ break;
+ case 'f':
+ case 'F':
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ if (tokenLen <= MaxSecondsFractionDigits)
+ {
+ long fraction = (dateTime.Ticks % Calendar.TicksPerSecond);
+ fraction = fraction / (long)Math.Pow(10, 7 - tokenLen);
+ if (ch == 'f')
+ {
+ result.Append(((int)fraction).ToString(fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture));
+ }
+ else
+ {
+ int effectiveDigits = tokenLen;
+ while (effectiveDigits > 0)
+ {
+ if (fraction % 10 == 0)
+ {
+ fraction = fraction / 10;
+ effectiveDigits--;
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (effectiveDigits > 0)
+ {
+ result.Append(((int)fraction).ToString(fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture));
+ }
+ else
+ {
+ // No fraction to emit, so see if we should remove decimal also.
+ if (result.Length > 0 && result[result.Length - 1] == '.')
+ {
+ result.Remove(result.Length - 1, 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ throw new FormatException(SR.Format_InvalidString);
+ }
+ break;
+ case 't':
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ if (tokenLen == 1)
+ {
+ if (dateTime.Hour < 12)
+ {
+ if (dtfi.AMDesignator.Length >= 1)
+ {
+ result.Append(dtfi.AMDesignator[0]);
+ }
+ }
+ else
+ {
+ if (dtfi.PMDesignator.Length >= 1)
+ {
+ result.Append(dtfi.PMDesignator[0]);
+ }
+ }
+ }
+ else
+ {
+ result.Append((dateTime.Hour < 12 ? dtfi.AMDesignator : dtfi.PMDesignator));
+ }
+ break;
+ case 'd':
+ //
+ // tokenLen == 1 : Day of month as digits with no leading zero.
+ // tokenLen == 2 : Day of month as digits with leading zero for single-digit months.
+ // tokenLen == 3 : Day of week as a three-leter abbreviation.
+ // tokenLen >= 4 : Day of week as its full name.
+ //
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ if (tokenLen <= 2)
+ {
+ int day = cal.GetDayOfMonth(dateTime);
+ if (isHebrewCalendar)
+ {
+ // For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values.
+ HebrewFormatDigits(result, day);
+ }
+ else
+ {
+ FormatDigits(result, day, tokenLen);
+ }
+ }
+ else
+ {
+ int dayOfWeek = (int)cal.GetDayOfWeek(dateTime);
+ result.Append(FormatDayOfWeek(dayOfWeek, tokenLen, dtfi));
+ }
+ bTimeOnly = false;
+ break;
+ case 'M':
+ //
+ // tokenLen == 1 : Month as digits with no leading zero.
+ // tokenLen == 2 : Month as digits with leading zero for single-digit months.
+ // tokenLen == 3 : Month as a three-letter abbreviation.
+ // tokenLen >= 4 : Month as its full name.
+ //
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ int month = cal.GetMonth(dateTime);
+ if (tokenLen <= 2)
+ {
+ if (isHebrewCalendar)
+ {
+ // For Hebrew calendar, we need to convert numbers to Hebrew text for yyyy, MM, and dd values.
+ HebrewFormatDigits(result, month);
+ }
+ else
+ {
+ FormatDigits(result, month, tokenLen);
+ }
+ }
+ else
+ {
+ if (isHebrewCalendar)
+ {
+ result.Append(FormatHebrewMonthName(dateTime, month, tokenLen, dtfi));
+ }
+ else
+ {
+ if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0 && tokenLen >= 4)
+ {
+ result.Append(
+ dtfi.internalGetMonthName(
+ month,
+ IsUseGenitiveForm(format, i, tokenLen, 'd') ? MonthNameStyles.Genitive : MonthNameStyles.Regular,
+ false));
+ }
+ else
+ {
+ result.Append(FormatMonth(month, tokenLen, dtfi));
+ }
+ }
+ }
+ bTimeOnly = false;
+ break;
+ case 'y':
+ // Notes about OS behavior:
+ // y: Always print (year % 100). No leading zero.
+ // yy: Always print (year % 100) with leading zero.
+ // yyy/yyyy/yyyyy/... : Print year value. No leading zero.
+
+ int year = cal.GetYear(dateTime);
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ if (dtfi.HasForceTwoDigitYears)
+ {
+ FormatDigits(result, year, tokenLen <= 2 ? tokenLen : 2);
+ }
+ else if (cal.ID == CalendarId.HEBREW)
+ {
+ HebrewFormatDigits(result, year);
+ }
+ else
+ {
+ if (tokenLen <= 2)
+ {
+ FormatDigits(result, year % 100, tokenLen);
+ }
+ else
+ {
+ String fmtPattern = "D" + tokenLen.ToString();
+ result.Append(year.ToString(fmtPattern, CultureInfo.InvariantCulture));
+ }
+ }
+ bTimeOnly = false;
+ break;
+ case 'z':
+ tokenLen = ParseRepeatPattern(format, i, ch);
+ FormatCustomizedTimeZone(dateTime, offset, format, tokenLen, bTimeOnly, result);
+ break;
+ case 'K':
+ tokenLen = 1;
+ FormatCustomizedRoundripTimeZone(dateTime, offset, result);
+ break;
+ case ':':
+ result.Append(dtfi.TimeSeparator);
+ tokenLen = 1;
+ break;
+ case '/':
+ result.Append(dtfi.DateSeparator);
+ tokenLen = 1;
+ break;
+ case '\'':
+ case '\"':
+ tokenLen = ParseQuoteString(format, i, result);
+ break;
+ case '%':
+ // Optional format character.
+ // For example, format string "%d" will print day of month
+ // without leading zero. Most of the cases, "%" can be ignored.
+ nextChar = ParseNextChar(format, i);
+ // nextChar will be -1 if we already reach the end of the format string.
+ // Besides, we will not allow "%%" appear in the pattern.
+ if (nextChar >= 0 && nextChar != (int)'%')
+ {
+ result.Append(FormatCustomized(dateTime, ((char)nextChar).ToString(), dtfi, offset));
+ tokenLen = 2;
+ }
+ else
+ {
+ //
+ // This means that '%' is at the end of the format string or
+ // "%%" appears in the format string.
+ //
+ throw new FormatException(SR.Format_InvalidString);
+ }
+ break;
+ case '\\':
+ // Escaped character. Can be used to insert character into the format string.
+ // For exmple, "\d" will insert the character 'd' into the string.
+ //
+ // NOTENOTE : we can remove this format character if we enforce the enforced quote
+ // character rule.
+ // That is, we ask everyone to use single quote or double quote to insert characters,
+ // then we can remove this character.
+ //
+ nextChar = ParseNextChar(format, i);
+ if (nextChar >= 0)
+ {
+ result.Append(((char)nextChar));
+ tokenLen = 2;
+ }
+ else
+ {
+ //
+ // This means that '\' is at the end of the formatting string.
+ //
+ throw new FormatException(SR.Format_InvalidString);
+ }
+ break;
+ default:
+ // NOTENOTE : we can remove this rule if we enforce the enforced quote
+ // character rule.
+ // That is, if we ask everyone to use single quote or double quote to insert characters,
+ // then we can remove this default block.
+ result.Append(ch);
+ tokenLen = 1;
+ break;
+ }
+ i += tokenLen;
+ }
+ return StringBuilderCache.GetStringAndRelease(result);
+ }
+
+
+ // output the 'z' famliy of formats, which output a the offset from UTC, e.g. "-07:30"
+ private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset, String format, Int32 tokenLen, Boolean timeOnly, StringBuilder result)
+ {
+ // See if the instance already has an offset
+ Boolean dateTimeFormat = (offset == NullOffset);
+ if (dateTimeFormat)
+ {
+ // No offset. The instance is a DateTime and the output should be the local time zone
+
+ if (timeOnly && dateTime.Ticks < Calendar.TicksPerDay)
+ {
+ // For time only format and a time only input, the time offset on 0001/01/01 is less
+ // accurate than the system's current offset because of daylight saving time.
+ offset = TimeZoneInfo.GetLocalUtcOffset(DateTime.Now, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+ }
+ else if (dateTime.Kind == DateTimeKind.Utc)
+ {
+ offset = TimeSpan.Zero;
+ }
+ else
+ {
+ offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+ }
+ }
+ if (offset >= TimeSpan.Zero)
+ {
+ result.Append('+');
+ }
+ else
+ {
+ result.Append('-');
+ // get a positive offset, so that you don't need a separate code path for the negative numbers.
+ offset = offset.Negate();
+ }
+
+ if (tokenLen <= 1)
+ {
+ // 'z' format e.g "-7"
+ result.AppendFormat(CultureInfo.InvariantCulture, "{0:0}", offset.Hours);
+ }
+ else
+ {
+ // 'zz' or longer format e.g "-07"
+ result.AppendFormat(CultureInfo.InvariantCulture, "{0:00}", offset.Hours);
+ if (tokenLen >= 3)
+ {
+ // 'zzz*' or longer format e.g "-07:30"
+ result.AppendFormat(CultureInfo.InvariantCulture, ":{0:00}", offset.Minutes);
+ }
+ }
+ }
+
+ // output the 'K' format, which is for round-tripping the data
+ private static void FormatCustomizedRoundripTimeZone(DateTime dateTime, TimeSpan offset, StringBuilder result)
+ {
+ // The objective of this format is to round trip the data in the type
+ // For DateTime it should round-trip the Kind value and preserve the time zone.
+ // DateTimeOffset instance, it should do so by using the internal time zone.
+
+ if (offset == NullOffset)
+ {
+ // source is a date time, so behavior depends on the kind.
+ switch (dateTime.Kind)
+ {
+ case DateTimeKind.Local:
+ // This should output the local offset, e.g. "-07:30"
+ offset = TimeZoneInfo.GetLocalUtcOffset(dateTime, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+ // fall through to shared time zone output code
+ break;
+ case DateTimeKind.Utc:
+ // The 'Z' constant is a marker for a UTC date
+ result.Append("Z");
+ return;
+ default:
+ // If the kind is unspecified, we output nothing here
+ return;
+ }
+ }
+ if (offset >= TimeSpan.Zero)
+ {
+ result.Append('+');
+ }
+ else
+ {
+ result.Append('-');
+ // get a positive offset, so that you don't need a separate code path for the negative numbers.
+ offset = offset.Negate();
+ }
+
+ AppendNumber(result, offset.Hours, 2);
+ result.Append(':');
+ AppendNumber(result, offset.Minutes, 2);
+ }
+
+
+ internal static String GetRealFormat(String format, DateTimeFormatInfo dtfi)
+ {
+ String realFormat = null;
+
+ switch (format[0])
+ {
+ case 'd': // Short Date
+ realFormat = dtfi.ShortDatePattern;
+ break;
+ case 'D': // Long Date
+ realFormat = dtfi.LongDatePattern;
+ break;
+ case 'f': // Full (long date + short time)
+ realFormat = dtfi.LongDatePattern + " " + dtfi.ShortTimePattern;
+ break;
+ case 'F': // Full (long date + long time)
+ realFormat = dtfi.FullDateTimePattern;
+ break;
+ case 'g': // General (short date + short time)
+ realFormat = dtfi.GeneralShortTimePattern;
+ break;
+ case 'G': // General (short date + long time)
+ realFormat = dtfi.GeneralLongTimePattern;
+ break;
+ case 'm':
+ case 'M': // Month/Day Date
+ realFormat = dtfi.MonthDayPattern;
+ break;
+ case 'o':
+ case 'O':
+ realFormat = RoundtripFormat;
+ break;
+ case 'r':
+ case 'R': // RFC 1123 Standard
+ realFormat = dtfi.RFC1123Pattern;
+ break;
+ case 's': // Sortable without Time Zone Info
+ realFormat = dtfi.SortableDateTimePattern;
+ break;
+ case 't': // Short Time
+ realFormat = dtfi.ShortTimePattern;
+ break;
+ case 'T': // Long Time
+ realFormat = dtfi.LongTimePattern;
+ break;
+ case 'u': // Universal with Sortable format
+ realFormat = dtfi.UniversalSortableDateTimePattern;
+ break;
+ case 'U': // Universal with Full (long date + long time) format
+ realFormat = dtfi.FullDateTimePattern;
+ break;
+ case 'y':
+ case 'Y': // Year/Month Date
+ realFormat = dtfi.YearMonthPattern;
+ break;
+ default:
+ throw new FormatException(SR.Format_InvalidString);
+ }
+ return (realFormat);
+ }
+
+
+ // Expand a pre-defined format string (like "D" for long date) to the real format that
+ // we are going to use in the date time parsing.
+ // This method also convert the dateTime if necessary (e.g. when the format is in Universal time),
+ // and change dtfi if necessary (e.g. when the format should use invariant culture).
+ //
+ private static String ExpandPredefinedFormat(String format, ref DateTime dateTime, ref DateTimeFormatInfo dtfi, ref TimeSpan offset)
+ {
+ switch (format[0])
+ {
+ case 'o':
+ case 'O': // Round trip format
+ dtfi = DateTimeFormatInfo.InvariantInfo;
+ break;
+ case 'r':
+ case 'R': // RFC 1123 Standard
+ if (offset != NullOffset)
+ {
+ // Convert to UTC invariants mean this will be in range
+ dateTime = dateTime - offset;
+ }
+ else if (dateTime.Kind == DateTimeKind.Local)
+ {
+ InvalidFormatForLocal(format, dateTime);
+ }
+ dtfi = DateTimeFormatInfo.InvariantInfo;
+ break;
+ case 's': // Sortable without Time Zone Info
+ dtfi = DateTimeFormatInfo.InvariantInfo;
+ break;
+ case 'u': // Universal time in sortable format.
+ if (offset != NullOffset)
+ {
+ // Convert to UTC invariants mean this will be in range
+ dateTime = dateTime - offset;
+ }
+ else if (dateTime.Kind == DateTimeKind.Local)
+ {
+ InvalidFormatForLocal(format, dateTime);
+ }
+ dtfi = DateTimeFormatInfo.InvariantInfo;
+ break;
+ case 'U': // Universal time in culture dependent format.
+ if (offset != NullOffset)
+ {
+ // This format is not supported by DateTimeOffset
+ throw new FormatException(SR.Format_InvalidString);
+ }
+ // Universal time is always in Greogrian calendar.
+ //
+ // Change the Calendar to be Gregorian Calendar.
+ //
+ dtfi = (DateTimeFormatInfo)dtfi.Clone();
+ if (dtfi.Calendar.GetType() != typeof(GregorianCalendar))
+ {
+ dtfi.Calendar = GregorianCalendar.GetDefaultInstance();
+ }
+ dateTime = dateTime.ToUniversalTime();
+ break;
+ }
+ format = GetRealFormat(format, dtfi);
+ return (format);
+ }
+
+ internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi)
+ {
+ return Format(dateTime, format, dtfi, NullOffset);
+ }
+
+
+ internal static String Format(DateTime dateTime, String format, DateTimeFormatInfo dtfi, TimeSpan offset)
+ {
+ Contract.Requires(dtfi != null);
+ if (format == null || format.Length == 0)
+ {
+ Boolean timeOnlySpecialCase = false;
+ if (dateTime.Ticks < Calendar.TicksPerDay)
+ {
+ // If the time is less than 1 day, consider it as time of day.
+ // Just print out the short time format.
+ //
+ // This is a workaround for VB, since they use ticks less then one day to be
+ // time of day. In cultures which use calendar other than Gregorian calendar, these
+ // alternative calendar may not support ticks less than a day.
+ // For example, Japanese calendar only supports date after 1868/9/8.
+ // This will pose a problem when people in VB get the time of day, and use it
+ // to call ToString(), which will use the general format (short date + long time).
+ // Since Japanese calendar does not support Gregorian year 0001, an exception will be
+ // thrown when we try to get the Japanese year for Gregorian year 0001.
+ // Therefore, the workaround allows them to call ToString() for time of day from a DateTime by
+ // formatting as ISO 8601 format.
+ switch (dtfi.Calendar.ID)
+ {
+ case CalendarId.JAPAN:
+ case CalendarId.TAIWAN:
+ case CalendarId.HIJRI:
+ case CalendarId.HEBREW:
+ case CalendarId.JULIAN:
+ case CalendarId.UMALQURA:
+ case CalendarId.PERSIAN:
+ timeOnlySpecialCase = true;
+ dtfi = DateTimeFormatInfo.InvariantInfo;
+ break;
+ }
+ }
+ if (offset == NullOffset)
+ {
+ // Default DateTime.ToString case.
+ if (timeOnlySpecialCase)
+ {
+ format = "s";
+ }
+ else
+ {
+ format = "G";
+ }
+ }
+ else
+ {
+ // Default DateTimeOffset.ToString case.
+ if (timeOnlySpecialCase)
+ {
+ format = RoundtripDateTimeUnfixed;
+ }
+ else
+ {
+ format = dtfi.DateTimeOffsetPattern;
+ }
+ }
+ }
+
+ if (format.Length == 1)
+ {
+ switch (format[0])
+ {
+ case 'O':
+ case 'o':
+ return FastFormatRoundtrip(dateTime, offset);
+ case 'R':
+ case 'r':
+ return FastFormatRfc1123(dateTime, offset, dtfi);
+ }
+
+ format = ExpandPredefinedFormat(format, ref dateTime, ref dtfi, ref offset);
+ }
+
+ return (FormatCustomized(dateTime, format, dtfi, offset));
+ }
+
+ internal static string FastFormatRfc1123(DateTime dateTime, TimeSpan offset, DateTimeFormatInfo dtfi)
+ {
+ // ddd, dd MMM yyyy HH:mm:ss GMT
+ const int Rfc1123FormatLength = 29;
+ StringBuilder result = StringBuilderCache.Acquire(Rfc1123FormatLength);
+
+ if (offset != NullOffset)
+ {
+ // Convert to UTC invariants
+ dateTime = dateTime - offset;
+ }
+
+ result.Append(InvariantAbbreviatedDayNames[(int)dateTime.DayOfWeek]);
+ result.Append(',');
+ result.Append(' ');
+ AppendNumber(result, dateTime.Day, 2);
+ result.Append(' ');
+ result.Append(InvariantAbbreviatedMonthNames[dateTime.Month - 1]);
+ result.Append(' ');
+ AppendNumber(result, dateTime.Year, 4);
+ result.Append(' ');
+ AppendHHmmssTimeOfDay(result, dateTime);
+ result.Append(' ');
+ result.Append(Gmt);
+
+ return StringBuilderCache.GetStringAndRelease(result);
+ }
+
+ internal static string FastFormatRoundtrip(DateTime dateTime, TimeSpan offset)
+ {
+ // yyyy-MM-ddTHH:mm:ss.fffffffK
+ const int roundTripFormatLength = 28;
+ StringBuilder result = StringBuilderCache.Acquire(roundTripFormatLength);
+
+ AppendNumber(result, dateTime.Year, 4);
+ result.Append('-');
+ AppendNumber(result, dateTime.Month, 2);
+ result.Append('-');
+ AppendNumber(result, dateTime.Day, 2);
+ result.Append('T');
+ AppendHHmmssTimeOfDay(result, dateTime);
+ result.Append('.');
+
+ long fraction = dateTime.Ticks % TimeSpan.TicksPerSecond;
+ AppendNumber(result, fraction, 7);
+
+ FormatCustomizedRoundripTimeZone(dateTime, offset, result);
+
+ return StringBuilderCache.GetStringAndRelease(result);
+ }
+
+ private static void AppendHHmmssTimeOfDay(StringBuilder result, DateTime dateTime)
+ {
+ // HH:mm:ss
+ AppendNumber(result, dateTime.Hour, 2);
+ result.Append(':');
+ AppendNumber(result, dateTime.Minute, 2);
+ result.Append(':');
+ AppendNumber(result, dateTime.Second, 2);
+ }
+
+ internal static void AppendNumber(StringBuilder builder, long val, int digits)
+ {
+ for (int i = 0; i < digits; i++)
+ {
+ builder.Append('0');
+ }
+
+ int index = 1;
+ while (val > 0 && index <= digits)
+ {
+ builder[builder.Length - index] = (char)('0' + (val % 10));
+ val = val / 10;
+ index++;
+ }
+
+ Debug.Assert(val == 0, "DateTimeFormat.AppendNumber(): digits less than size of val");
+ }
+
+ internal static String[] GetAllDateTimes(DateTime dateTime, char format, DateTimeFormatInfo dtfi)
+ {
+ Contract.Requires(dtfi != null);
+ String[] allFormats = null;
+ String[] results = null;
+
+ switch (format)
+ {
+ case 'd':
+ case 'D':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ case 'm':
+ case 'M':
+ case 't':
+ case 'T':
+ case 'y':
+ case 'Y':
+ allFormats = dtfi.GetAllDateTimePatterns(format);
+ results = new String[allFormats.Length];
+ for (int i = 0; i < allFormats.Length; i++)
+ {
+ results[i] = Format(dateTime, allFormats[i], dtfi);
+ }
+ break;
+ case 'U':
+ DateTime universalTime = dateTime.ToUniversalTime();
+ allFormats = dtfi.GetAllDateTimePatterns(format);
+ results = new String[allFormats.Length];
+ for (int i = 0; i < allFormats.Length; i++)
+ {
+ results[i] = Format(universalTime, allFormats[i], dtfi);
+ }
+ break;
+ //
+ // The following ones are special cases because these patterns are read-only in
+ // DateTimeFormatInfo.
+ //
+ case 'r':
+ case 'R':
+ case 'o':
+ case 'O':
+ case 's':
+ case 'u':
+ results = new String[] { Format(dateTime, new String(format, 1), dtfi) };
+ break;
+ default:
+ throw new FormatException(SR.Format_InvalidString);
+ }
+ return (results);
+ }
+
+ internal static String[] GetAllDateTimes(DateTime dateTime, DateTimeFormatInfo dtfi)
+ {
+ List<String> results = new List<String>(DEFAULT_ALL_DATETIMES_SIZE);
+
+ for (int i = 0; i < allStandardFormats.Length; i++)
+ {
+ String[] strings = GetAllDateTimes(dateTime, allStandardFormats[i], dtfi);
+ for (int j = 0; j < strings.Length; j++)
+ {
+ results.Add(strings[j]);
+ }
+ }
+ String[] value = new String[results.Count];
+ results.CopyTo(0, value, 0, results.Count);
+ return (value);
+ }
+
+ // This is a placeholder for an MDA to detect when the user is using a
+ // local DateTime with a format that will be interpreted as UTC.
+ internal static void InvalidFormatForLocal(String format, DateTime dateTime)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs
new file mode 100644
index 0000000000..d3f1ea9a45
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfo.cs
@@ -0,0 +1,3028 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
+
+namespace System.Globalization
+{
+ //
+ // Flags used to indicate different styles of month names.
+ // This is an internal flag used by internalGetMonthName().
+ // Use flag here in case that we need to provide a combination of these styles
+ // (such as month name of a leap year in genitive form. Not likely for now,
+ // but would like to keep the option open).
+ //
+
+ [Flags]
+ internal enum MonthNameStyles
+ {
+ Regular = 0x00000000,
+ Genitive = 0x00000001,
+ LeapYear = 0x00000002,
+ }
+
+ //
+ // Flags used to indicate special rule used in parsing/formatting
+ // for a specific DateTimeFormatInfo instance.
+ // This is an internal flag.
+ //
+ // This flag is different from MonthNameStyles because this flag
+ // can be expanded to accomodate parsing behaviors like CJK month names
+ // or alternative month names, etc.
+
+ [Flags]
+ internal enum DateTimeFormatFlags
+ {
+ None = 0x00000000,
+ UseGenitiveMonth = 0x00000001,
+ UseLeapYearMonth = 0x00000002,
+ UseSpacesInMonthNames = 0x00000004, // Has spaces or non-breaking space in the month names.
+ UseHebrewRule = 0x00000008, // Format/Parse using the Hebrew calendar rule.
+ UseSpacesInDayNames = 0x00000010, // Has spaces or non-breaking space in the day names.
+ UseDigitPrefixInTokens = 0x00000020, // Has token starting with numbers.
+
+ NotInitialized = -1,
+ }
+
+
+ [Serializable]
+ public sealed class DateTimeFormatInfo : IFormatProvider, ICloneable
+ {
+ // cache for the invariant culture.
+ // invariantInfo is constant irrespective of your current culture.
+ private static volatile DateTimeFormatInfo s_invariantInfo;
+
+ // an index which points to a record in Culture Data Table.
+ [NonSerialized]
+ private CultureData _cultureData;
+
+ // The culture name used to create this DTFI.
+
+ [OptionalField(VersionAdded = 2)]
+ private String _name = null;
+
+ // The language name of the culture used to create this DTFI.
+ [NonSerialized]
+ private String _langName = null;
+
+ // CompareInfo usually used by the parser.
+ [NonSerialized]
+ private CompareInfo _compareInfo = null;
+
+ // Culture matches current DTFI. mainly used for string comparisons during parsing.
+ [NonSerialized]
+ private CultureInfo _cultureInfo = null;
+
+ //
+ // Caches for various properties.
+ //
+
+ private String amDesignator = null;
+ private String pmDesignator = null;
+
+ private String dateSeparator = null; // derived from short date (whidbey expects, arrowhead doesn't)
+
+ private String generalShortTimePattern = null; // short date + short time (whidbey expects, arrowhead doesn't)
+
+ private String generalLongTimePattern = null; // short date + long time (whidbey expects, arrowhead doesn't)
+
+ private String timeSeparator = null; // derived from long time (whidbey expects, arrowhead doesn't)
+ private String monthDayPattern = null;
+ // added in .NET Framework Release {2.0SP1/3.0SP1/3.5RTM}
+ private String dateTimeOffsetPattern = null;
+
+ //
+ // The following are constant values.
+ //
+ private const String rfc1123Pattern = "ddd, dd MMM yyyy HH':'mm':'ss 'GMT'";
+
+ // The sortable pattern is based on ISO 8601.
+ private const String sortableDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss";
+ private const String universalSortableDateTimePattern = "yyyy'-'MM'-'dd HH':'mm':'ss'Z'";
+
+ //
+ // The following are affected by calendar settings.
+ //
+ private Calendar calendar = null;
+
+ private int firstDayOfWeek = -1;
+ private int calendarWeekRule = -1;
+
+
+ private String fullDateTimePattern = null; // long date + long time (whidbey expects, arrowhead doesn't)
+
+ private String[] abbreviatedDayNames = null;
+
+
+ private String[] m_superShortDayNames = null;
+
+ private String[] dayNames = null;
+ private String[] abbreviatedMonthNames = null;
+ private String[] monthNames = null;
+ // Cache the genitive month names that we retrieve from the data table.
+
+ private String[] genitiveMonthNames = null;
+
+ // Cache the abbreviated genitive month names that we retrieve from the data table.
+
+ private String[] m_genitiveAbbreviatedMonthNames = null;
+
+ // Cache the month names of a leap year that we retrieve from the data table.
+
+ private String[] leapYearMonthNames = null;
+
+ // For our "patterns" arrays we have 2 variables, a string and a string[]
+ //
+ // The string[] contains the list of patterns, EXCEPT the default may not be included.
+ // The string contains the default pattern.
+ // When we initially construct our string[], we set the string to string[0]
+
+ // The "default" Date/time patterns
+ private String longDatePattern = null;
+ private String shortDatePattern = null;
+ private String yearMonthPattern = null;
+ private String longTimePattern = null;
+ private String shortTimePattern = null;
+
+ [OptionalField(VersionAdded = 3)]
+ private String[] allYearMonthPatterns = null;
+
+ private String[] allShortDatePatterns = null;
+ private String[] allLongDatePatterns = null;
+ private String[] allShortTimePatterns = null;
+ private String[] allLongTimePatterns = null;
+
+ // Cache the era names for this DateTimeFormatInfo instance.
+ private String[] m_eraNames = null;
+ private String[] m_abbrevEraNames = null;
+ private String[] m_abbrevEnglishEraNames = null;
+
+ private CalendarId[] optionalCalendars = null;
+
+ private const int DEFAULT_ALL_DATETIMES_SIZE = 132;
+
+ // CultureInfo updates this
+ internal bool _isReadOnly = false;
+
+ // This flag gives hints about if formatting/parsing should perform special code path for things like
+ // genitive form or leap year month names.
+
+ private DateTimeFormatFlags formatFlags = DateTimeFormatFlags.NotInitialized;
+
+ private String CultureName
+ {
+ get
+ {
+ if (_name == null)
+ {
+ _name = _cultureData.CultureName;
+ }
+ return (_name);
+ }
+ }
+
+ private CultureInfo Culture
+ {
+ get
+ {
+ if (_cultureInfo == null)
+ {
+ _cultureInfo = CultureInfo.GetCultureInfo(this.CultureName);
+ }
+ return _cultureInfo;
+ }
+ }
+
+ // TODO: This ignores other cultures that might want to do something similar
+ private String LanguageName
+ {
+ get
+ {
+ if (_langName == null)
+ {
+ _langName = _cultureData.SISO639LANGNAME;
+ }
+ return (_langName);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Create an array of string which contains the abbreviated day names.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ private String[] internalGetAbbreviatedDayOfWeekNames()
+ {
+ if (this.abbreviatedDayNames == null)
+ {
+ // Get the abbreviated day names for our current calendar
+ this.abbreviatedDayNames = _cultureData.AbbreviatedDayNames(Calendar.ID);
+ Debug.Assert(this.abbreviatedDayNames.Length == 7, "[DateTimeFormatInfo.GetAbbreviatedDayOfWeekNames] Expected 7 day names in a week");
+ }
+ return (this.abbreviatedDayNames);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Action: Returns the string array of the one-letter day of week names.
+ // Returns:
+ // an array of one-letter day of week names
+ // Arguments:
+ // None
+ // Exceptions:
+ // None
+ //
+ ////////////////////////////////////////////////////////////////////////
+
+ private String[] internalGetSuperShortDayNames()
+ {
+ if (this.m_superShortDayNames == null)
+ {
+ // Get the super short day names for our current calendar
+ this.m_superShortDayNames = _cultureData.SuperShortDayNames(Calendar.ID);
+ Debug.Assert(this.m_superShortDayNames.Length == 7, "[DateTimeFormatInfo.internalGetSuperShortDayNames] Expected 7 day names in a week");
+ }
+ return (this.m_superShortDayNames);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Create an array of string which contains the day names.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ private String[] internalGetDayOfWeekNames()
+ {
+ if (this.dayNames == null)
+ {
+ // Get the day names for our current calendar
+ this.dayNames = _cultureData.DayNames(Calendar.ID);
+ Debug.Assert(this.dayNames.Length == 7, "[DateTimeFormatInfo.GetDayOfWeekNames] Expected 7 day names in a week");
+ }
+ return (this.dayNames);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Create an array of string which contains the abbreviated month names.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ private String[] internalGetAbbreviatedMonthNames()
+ {
+ if (this.abbreviatedMonthNames == null)
+ {
+ // Get the month names for our current calendar
+ this.abbreviatedMonthNames = _cultureData.AbbreviatedMonthNames(Calendar.ID);
+ Debug.Assert(this.abbreviatedMonthNames.Length == 12 || this.abbreviatedMonthNames.Length == 13,
+ "[DateTimeFormatInfo.GetAbbreviatedMonthNames] Expected 12 or 13 month names in a year");
+ }
+ return (this.abbreviatedMonthNames);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Create an array of string which contains the month names.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ private String[] internalGetMonthNames()
+ {
+ if (this.monthNames == null)
+ {
+ // Get the month names for our current calendar
+ this.monthNames = _cultureData.MonthNames(Calendar.ID);
+ Debug.Assert(this.monthNames.Length == 12 || this.monthNames.Length == 13,
+ "[DateTimeFormatInfo.GetMonthNames] Expected 12 or 13 month names in a year");
+ }
+
+ return (this.monthNames);
+ }
+
+
+ //
+ // Invariant DateTimeFormatInfo doesn't have user-overriden values
+ // Default calendar is gregorian
+ public DateTimeFormatInfo()
+ : this(CultureInfo.InvariantCulture._cultureData, GregorianCalendar.GetDefaultInstance())
+ {
+ }
+
+ internal DateTimeFormatInfo(CultureData cultureData, Calendar cal)
+ {
+ Debug.Assert(cultureData != null);
+ Debug.Assert(cal != null);
+
+ // Remember our culture
+ _cultureData = cultureData;
+
+ this.Calendar = cal;
+ }
+
+ private void InitializeOverridableProperties(CultureData cultureData, CalendarId calendarId)
+ {
+ Debug.Assert(cultureData != null);
+ Debug.Assert(calendarId != CalendarId.UNINITIALIZED_VALUE, "[DateTimeFormatInfo.Populate] Expected initalized calendarId");
+
+ if (this.firstDayOfWeek == -1) { this.firstDayOfWeek = cultureData.IFIRSTDAYOFWEEK; }
+ if (this.calendarWeekRule == -1) { this.calendarWeekRule = cultureData.IFIRSTWEEKOFYEAR; }
+
+ if (this.amDesignator == null) { this.amDesignator = cultureData.SAM1159; }
+ if (this.pmDesignator == null) { this.pmDesignator = cultureData.SPM2359; }
+ if (this.timeSeparator == null) { this.timeSeparator = cultureData.TimeSeparator; }
+ if (this.dateSeparator == null) { this.dateSeparator = cultureData.DateSeparator(calendarId); }
+
+ this.allLongTimePatterns = _cultureData.LongTimes;
+ Debug.Assert(this.allLongTimePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some long time patterns");
+
+ this.allShortTimePatterns = _cultureData.ShortTimes;
+ Debug.Assert(this.allShortTimePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some short time patterns");
+
+ this.allLongDatePatterns = cultureData.LongDates(calendarId);
+ Debug.Assert(this.allLongDatePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some long date patterns");
+
+ this.allShortDatePatterns = cultureData.ShortDates(calendarId);
+ Debug.Assert(this.allShortDatePatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some short date patterns");
+
+ this.allYearMonthPatterns = cultureData.YearMonths(calendarId);
+ Debug.Assert(this.allYearMonthPatterns.Length > 0, "[DateTimeFormatInfo.Populate] Expected some year month patterns");
+ }
+
+ [OptionalField(VersionAdded = 1)]
+ private bool _useUserOverride;
+
+ // This was synthesized by Whidbey so we knew what words might appear in the middle of a date string
+ // Now we always synthesize so its not helpful
+
+ internal String[] m_dateWords = null;
+
+ [OnSerializing]
+ private void OnSerializing(StreamingContext ctx)
+ {
+ _name = this.CultureName; // make sure the _name is initialized.
+ _useUserOverride = _cultureData.UseUserOverride;
+
+ // Important to initialize these fields otherwise we may run into exception when deserializing on Whidbey
+ // because Whidbey try to initialize some of these fields using calendar data which could be null values
+ // and then we get exceptions. So we call the accessors to force the caches to get loaded.
+ Object o;
+ o = this.LongTimePattern;
+ o = this.LongDatePattern;
+ o = this.ShortTimePattern;
+ o = this.ShortDatePattern;
+ o = this.YearMonthPattern;
+ o = this.AllLongTimePatterns;
+ o = this.AllLongDatePatterns;
+ o = this.AllShortTimePatterns;
+ o = this.AllShortDatePatterns;
+ o = this.AllYearMonthPatterns;
+ }
+
+ [OnDeserialized]
+ private void OnDeserialized(StreamingContext ctx)
+ {
+ if (_name != null)
+ {
+ _cultureData = CultureData.GetCultureData(_name, _useUserOverride);
+ if (_cultureData == null)
+ {
+ throw new CultureNotFoundException("_name", _name, SR.Argument_CultureNotSupported);
+ }
+ }
+
+ if (calendar == null)
+ {
+ calendar = (Calendar)GregorianCalendar.GetDefaultInstance().Clone();
+ calendar.SetReadOnlyState(_isReadOnly);
+ }
+
+ InitializeOverridableProperties(_cultureData, calendar.ID);
+
+ //
+ // turn off read only state till we finish initializing all fields and then store read only state after we are done.
+ //
+ bool isReadOnly = _isReadOnly;
+ _isReadOnly = false;
+
+ // If we deserialized defaults ala Whidbey, make sure they're still defaults
+ // Whidbey's arrays could get a bit mixed up.
+ if (longDatePattern != null) this.LongDatePattern = longDatePattern;
+ if (shortDatePattern != null) this.ShortDatePattern = shortDatePattern;
+ if (yearMonthPattern != null) this.YearMonthPattern = yearMonthPattern;
+ if (longTimePattern != null) this.LongTimePattern = longTimePattern;
+ if (shortTimePattern != null) this.ShortTimePattern = shortTimePattern;
+
+ _isReadOnly = isReadOnly;
+ }
+
+ // Returns a default DateTimeFormatInfo that will be universally
+ // supported and constant irrespective of the current culture.
+ // Used by FromString methods.
+ //
+
+ public static DateTimeFormatInfo InvariantInfo
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<DateTimeFormatInfo>() != null);
+ if (s_invariantInfo == null)
+ {
+ DateTimeFormatInfo info = new DateTimeFormatInfo();
+ info.Calendar.SetReadOnlyState(true);
+ info._isReadOnly = true;
+ s_invariantInfo = info;
+ }
+ return (s_invariantInfo);
+ }
+ }
+
+ // Returns the current culture's DateTimeFormatInfo. Used by Parse methods.
+ //
+
+ public static DateTimeFormatInfo CurrentInfo
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<DateTimeFormatInfo>() != null);
+ System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CurrentCulture;
+ if (!culture._isInherited)
+ {
+ DateTimeFormatInfo info = culture.dateTimeInfo;
+ if (info != null)
+ {
+ return info;
+ }
+ }
+ return (DateTimeFormatInfo)culture.GetFormat(typeof(DateTimeFormatInfo));
+ }
+ }
+
+
+ public static DateTimeFormatInfo GetInstance(IFormatProvider provider)
+ {
+ // Fast case for a regular CultureInfo
+ DateTimeFormatInfo info;
+ CultureInfo cultureProvider = provider as CultureInfo;
+ if (cultureProvider != null && !cultureProvider._isInherited)
+ {
+ return cultureProvider.DateTimeFormat;
+ }
+ // Fast case for a DTFI;
+ info = provider as DateTimeFormatInfo;
+ if (info != null)
+ {
+ return info;
+ }
+ // Wasn't cultureInfo or DTFI, do it the slower way
+ if (provider != null)
+ {
+ info = provider.GetFormat(typeof(DateTimeFormatInfo)) as DateTimeFormatInfo;
+ if (info != null)
+ {
+ return info;
+ }
+ }
+ // Couldn't get anything, just use currentInfo as fallback
+ return CurrentInfo;
+ }
+
+
+ public Object GetFormat(Type formatType)
+ {
+ return (formatType == typeof(DateTimeFormatInfo) ? this : null);
+ }
+
+
+ public Object Clone()
+ {
+ DateTimeFormatInfo n = (DateTimeFormatInfo)MemberwiseClone();
+ // We can use the data member calendar in the setter, instead of the property Calendar,
+ // since the cloned copy should have the same state as the original copy.
+ n.calendar = (Calendar)this.Calendar.Clone();
+ n._isReadOnly = false;
+ return n;
+ }
+
+
+ public String AMDesignator
+ {
+ get
+ {
+ if (this.amDesignator == null)
+ {
+ this.amDesignator = _cultureData.SAM1159;
+ }
+ Debug.Assert(this.amDesignator != null, "DateTimeFormatInfo.AMDesignator, amDesignator != null");
+ return (this.amDesignator);
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+ ClearTokenHashTable();
+ amDesignator = value;
+ }
+ }
+
+
+ public Calendar Calendar
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<Calendar>() != null);
+
+ Debug.Assert(this.calendar != null, "DateTimeFormatInfo.Calendar: calendar != null");
+ return (this.calendar);
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), SR.ArgumentNull_Obj);
+ }
+ Contract.EndContractBlock();
+ if (value == calendar)
+ {
+ return;
+ }
+
+ for (int i = 0; i < this.OptionalCalendars.Length; i++)
+ {
+ if (this.OptionalCalendars[i] == value.ID)
+ {
+ // We can use this one, so do so.
+
+ // Clean related properties if we already had a calendar set
+ if (calendar != null)
+ {
+ // clean related properties which are affected by the calendar setting,
+ // so that they will be refreshed when they are accessed next time.
+ //
+
+ // These properites are in the order as appearing in calendar.xml.
+ m_eraNames = null;
+ m_abbrevEraNames = null;
+ m_abbrevEnglishEraNames = null;
+
+ monthDayPattern = null;
+
+ dayNames = null;
+ abbreviatedDayNames = null;
+ m_superShortDayNames = null;
+ monthNames = null;
+ abbreviatedMonthNames = null;
+ genitiveMonthNames = null;
+ m_genitiveAbbreviatedMonthNames = null;
+ leapYearMonthNames = null;
+ formatFlags = DateTimeFormatFlags.NotInitialized;
+
+ allShortDatePatterns = null;
+ allLongDatePatterns = null;
+ allYearMonthPatterns = null;
+ dateTimeOffsetPattern = null;
+
+ // The defaults need reset as well:
+ longDatePattern = null;
+ shortDatePattern = null;
+ yearMonthPattern = null;
+
+ // These properies are not in the OS data, but they are dependent on the values like shortDatePattern.
+ fullDateTimePattern = null; // Long date + long time
+ generalShortTimePattern = null; // short date + short time
+ generalLongTimePattern = null; // short date + long time
+
+ // Derived item that changes
+ dateSeparator = null;
+
+ // We don't need to do these because they are not changed by changing calendar
+ // amDesignator
+ // pmDesignator
+ // timeSeparator
+ // longTimePattern
+ // firstDayOfWeek
+ // calendarWeekRule
+
+ // remember to reload tokens
+ ClearTokenHashTable();
+ }
+
+ // Remember the new calendar
+ calendar = value;
+ InitializeOverridableProperties(_cultureData, calendar.ID);
+
+ // We succeeded, return
+ return;
+ }
+ }
+
+ // The assigned calendar is not a valid calendar for this culture, throw
+ throw new ArgumentOutOfRangeException(nameof(value), SR.Argument_InvalidCalendar);
+ }
+ }
+
+ private CalendarId[] OptionalCalendars
+ {
+ get
+ {
+ if (this.optionalCalendars == null)
+ {
+ this.optionalCalendars = _cultureData.CalendarIds;
+ }
+ return (this.optionalCalendars);
+ }
+ }
+
+ /*=================================GetEra==========================
+ **Action: Get the era value by parsing the name of the era.
+ **Returns: The era value for the specified era name.
+ ** -1 if the name of the era is not valid or not supported.
+ **Arguments: eraName the name of the era.
+ **Exceptions: None.
+ ============================================================================*/
+
+
+ public int GetEra(String eraName)
+ {
+ if (eraName == null)
+ {
+ throw new ArgumentNullException(nameof(eraName),
+ SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+
+ // The Era Name and Abbreviated Era Name
+ // for Taiwan Calendar on non-Taiwan SKU returns empty string (which
+ // would be matched below) but we don't want the empty string to give
+ // us an Era number
+ // confer 85900 DTFI.GetEra("") should fail on all cultures
+ if (eraName.Length == 0)
+ {
+ return (-1);
+ }
+
+ // The following is based on the assumption that the era value is starting from 1, and has a
+ // serial values.
+ // If that ever changes, the code has to be changed.
+
+ // The calls to String.Compare should use the current culture for the string comparisons, but the
+ // invariant culture when comparing against the english names.
+ for (int i = 0; i < EraNames.Length; i++)
+ {
+ // Compare the era name in a case-insensitive way for the appropriate culture.
+ if (m_eraNames[i].Length > 0)
+ {
+ if (this.Culture.CompareInfo.Compare(eraName, m_eraNames[i], CompareOptions.IgnoreCase) == 0)
+ {
+ return (i + 1);
+ }
+ }
+ }
+ for (int i = 0; i < AbbreviatedEraNames.Length; i++)
+ {
+ // Compare the abbreviated era name in a case-insensitive way for the appropriate culture.
+ if (this.Culture.CompareInfo.Compare(eraName, m_abbrevEraNames[i], CompareOptions.IgnoreCase) == 0)
+ {
+ return (i + 1);
+ }
+ }
+ for (int i = 0; i < AbbreviatedEnglishEraNames.Length; i++)
+ {
+ // this comparison should use the InvariantCulture. The English name could have linguistically
+ // interesting characters.
+ if (CultureInfo.InvariantCulture.CompareInfo.Compare(eraName, m_abbrevEnglishEraNames[i], CompareOptions.IgnoreCase) == 0)
+ {
+ return (i + 1);
+ }
+ }
+ return (-1);
+ }
+
+
+ internal String[] EraNames
+ {
+ get
+ {
+ if (this.m_eraNames == null)
+ {
+ this.m_eraNames = _cultureData.EraNames(Calendar.ID); ;
+ }
+ return (this.m_eraNames);
+ }
+ }
+
+ /*=================================GetEraName==========================
+ **Action: Get the name of the era for the specified era value.
+ **Returns: The name of the specified era.
+ **Arguments:
+ ** era the era value.
+ **Exceptions:
+ ** ArguementException if the era valie is invalid.
+ ============================================================================*/
+
+ // Era names are 1 indexed
+ public String GetEraName(int era)
+ {
+ if (era == Calendar.CurrentEra)
+ {
+ era = Calendar.CurrentEraValue;
+ }
+
+ // The following is based on the assumption that the era value is starting from 1, and has a
+ // serial values.
+ // If that ever changes, the code has to be changed.
+ if ((--era) < EraNames.Length && (era >= 0))
+ {
+ return (m_eraNames[era]);
+ }
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+
+ internal String[] AbbreviatedEraNames
+ {
+ get
+ {
+ if (this.m_abbrevEraNames == null)
+ {
+ this.m_abbrevEraNames = _cultureData.AbbrevEraNames(Calendar.ID);
+ }
+ return (this.m_abbrevEraNames);
+ }
+ }
+
+ // Era names are 1 indexed
+ public String GetAbbreviatedEraName(int era)
+ {
+ if (AbbreviatedEraNames.Length == 0)
+ {
+ // If abbreviation era name is not used in this culture,
+ // return the full era name.
+ return (GetEraName(era));
+ }
+ if (era == Calendar.CurrentEra)
+ {
+ era = Calendar.CurrentEraValue;
+ }
+ if ((--era) < m_abbrevEraNames.Length && (era >= 0))
+ {
+ return (m_abbrevEraNames[era]);
+ }
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+
+ internal String[] AbbreviatedEnglishEraNames
+ {
+ get
+ {
+ if (this.m_abbrevEnglishEraNames == null)
+ {
+ Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.AbbreviatedEnglishEraNames] Expected Calendar.ID > 0");
+ this.m_abbrevEnglishEraNames = _cultureData.AbbreviatedEnglishEraNames(Calendar.ID);
+ }
+ return (this.m_abbrevEnglishEraNames);
+ }
+ }
+
+ // Note that cultureData derives this from the short date format (unless someone's set this previously)
+ // Note that this property is quite undesirable.
+ public string DateSeparator
+ {
+ get
+ {
+ if (dateSeparator == null)
+ {
+ dateSeparator = _cultureData.DateSeparator(Calendar.ID);
+ }
+ Debug.Assert(this.dateSeparator != null, "DateTimeFormatInfo.DateSeparator, dateSeparator != null");
+ return dateSeparator;
+ }
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+ ClearTokenHashTable();
+ dateSeparator = value;
+ }
+ }
+
+ public DayOfWeek FirstDayOfWeek
+ {
+ get
+ {
+ if (this.firstDayOfWeek == -1)
+ {
+ this.firstDayOfWeek = _cultureData.IFIRSTDAYOFWEEK;
+ }
+ Debug.Assert(this.firstDayOfWeek != -1, "DateTimeFormatInfo.FirstDayOfWeek, firstDayOfWeek != -1");
+
+ return ((DayOfWeek)this.firstDayOfWeek);
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value >= DayOfWeek.Sunday && value <= DayOfWeek.Saturday)
+ {
+ firstDayOfWeek = (int)value;
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(value), SR.Format(SR.ArgumentOutOfRange_Range,
+ DayOfWeek.Sunday, DayOfWeek.Saturday));
+ }
+ }
+ }
+
+ public CalendarWeekRule CalendarWeekRule
+ {
+ get
+ {
+ if (this.calendarWeekRule == -1)
+ {
+ this.calendarWeekRule = _cultureData.IFIRSTWEEKOFYEAR;
+ }
+ Debug.Assert(this.calendarWeekRule != -1, "DateTimeFormatInfo.CalendarWeekRule, calendarWeekRule != -1");
+ return ((CalendarWeekRule)this.calendarWeekRule);
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value >= CalendarWeekRule.FirstDay && value <= CalendarWeekRule.FirstFourDayWeek)
+ {
+ calendarWeekRule = (int)value;
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(value), SR.Format(SR.ArgumentOutOfRange_Range,
+ CalendarWeekRule.FirstDay, CalendarWeekRule.FirstFourDayWeek));
+ }
+ }
+ }
+
+ public String FullDateTimePattern
+ {
+ get
+ {
+ if (fullDateTimePattern == null)
+ {
+ fullDateTimePattern = LongDatePattern + " " + LongTimePattern;
+ }
+ return (fullDateTimePattern);
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+ fullDateTimePattern = value;
+ }
+ }
+
+
+ // For our "patterns" arrays we have 2 variables, a string and a string[]
+ //
+ // The string[] contains the list of patterns, EXCEPT the default may not be included.
+ // The string contains the default pattern.
+ // When we initially construct our string[], we set the string to string[0]
+ public String LongDatePattern
+ {
+ get
+ {
+ // Initialize our long date pattern from the 1st array value if not set
+ if (this.longDatePattern == null)
+ {
+ // Initialize our data
+ this.longDatePattern = this.UnclonedLongDatePatterns[0];
+ }
+
+ return this.longDatePattern;
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+
+ // Remember the new string
+ this.longDatePattern = value;
+
+ // Clear the token hash table
+ ClearTokenHashTable();
+
+ // Clean up cached values that will be affected by this property.
+ this.fullDateTimePattern = null;
+ }
+ }
+
+ // For our "patterns" arrays we have 2 variables, a string and a string[]
+ //
+ // The string[] contains the list of patterns, EXCEPT the default may not be included.
+ // The string contains the default pattern.
+ // When we initially construct our string[], we set the string to string[0]
+ public String LongTimePattern
+ {
+ get
+ {
+ // Initialize our long time pattern from the 1st array value if not set
+ if (this.longTimePattern == null)
+ {
+ // Initialize our data
+ this.longTimePattern = this.UnclonedLongTimePatterns[0];
+ }
+
+ return this.longTimePattern;
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+
+ // Remember the new string
+ this.longTimePattern = value;
+
+ // Clear the token hash table
+ ClearTokenHashTable();
+
+ // Clean up cached values that will be affected by this property.
+ this.fullDateTimePattern = null; // Full date = long date + long Time
+ this.generalLongTimePattern = null; // General long date = short date + long Time
+ this.dateTimeOffsetPattern = null;
+ }
+ }
+
+
+ // Note: just to be confusing there's only 1 month day pattern, not a whole list
+ public String MonthDayPattern
+ {
+ get
+ {
+ if (this.monthDayPattern == null)
+ {
+ Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.MonthDayPattern] Expected calID > 0");
+ this.monthDayPattern = _cultureData.MonthDay(Calendar.ID);
+ }
+ Debug.Assert(this.monthDayPattern != null, "DateTimeFormatInfo.MonthDayPattern, monthDayPattern != null");
+ return (this.monthDayPattern);
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+
+ this.monthDayPattern = value;
+ }
+ }
+
+
+ public String PMDesignator
+ {
+ get
+ {
+ if (this.pmDesignator == null)
+ {
+ this.pmDesignator = _cultureData.SPM2359;
+ }
+ Debug.Assert(this.pmDesignator != null, "DateTimeFormatInfo.PMDesignator, pmDesignator != null");
+ return (this.pmDesignator);
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+ ClearTokenHashTable();
+
+ pmDesignator = value;
+ }
+ }
+
+
+ public String RFC1123Pattern
+ {
+ get
+ {
+ return (rfc1123Pattern);
+ }
+ }
+
+ // For our "patterns" arrays we have 2 variables, a string and a string[]
+ //
+ // The string[] contains the list of patterns, EXCEPT the default may not be included.
+ // The string contains the default pattern.
+ // When we initially construct our string[], we set the string to string[0]
+ public String ShortDatePattern
+ {
+ get
+ {
+ // Initialize our short date pattern from the 1st array value if not set
+ if (this.shortDatePattern == null)
+ {
+ // Initialize our data
+ this.shortDatePattern = this.UnclonedShortDatePatterns[0];
+ }
+
+ return this.shortDatePattern;
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_String);
+ Contract.EndContractBlock();
+
+ // Remember the new string
+ this.shortDatePattern = value;
+
+ // Clear the token hash table, note that even short dates could require this
+ ClearTokenHashTable();
+
+ // Clean up cached values that will be affected by this property.
+ generalLongTimePattern = null; // General long time = short date + long time
+ generalShortTimePattern = null; // General short time = short date + short Time
+ dateTimeOffsetPattern = null;
+ }
+ }
+
+
+ // For our "patterns" arrays we have 2 variables, a string and a string[]
+ //
+ // The string[] contains the list of patterns, EXCEPT the default may not be included.
+ // The string contains the default pattern.
+ // When we initially construct our string[], we set the string to string[0]
+ public String ShortTimePattern
+ {
+ get
+ {
+ // Initialize our short time pattern from the 1st array value if not set
+ if (this.shortTimePattern == null)
+ {
+ // Initialize our data
+ this.shortTimePattern = this.UnclonedShortTimePatterns[0];
+ }
+ return this.shortTimePattern;
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+
+ // Remember the new string
+ this.shortTimePattern = value;
+
+ // Clear the token hash table, note that even short times could require this
+ ClearTokenHashTable();
+
+ // Clean up cached values that will be affected by this property.
+ generalShortTimePattern = null; // General short date = short date + short time.
+ }
+ }
+
+
+ public String SortableDateTimePattern
+ {
+ get
+ {
+ return (sortableDateTimePattern);
+ }
+ }
+
+ /*=================================GeneralShortTimePattern=====================
+ **Property: Return the pattern for 'g' general format: shortDate + short time
+ **Note: This is used by DateTimeFormat.cs to get the pattern for 'g'
+ ** We put this internal property here so that we can avoid doing the
+ ** concatation every time somebody asks for the general format.
+ ==============================================================================*/
+
+ internal String GeneralShortTimePattern
+ {
+ get
+ {
+ if (generalShortTimePattern == null)
+ {
+ generalShortTimePattern = ShortDatePattern + " " + ShortTimePattern;
+ }
+ return (generalShortTimePattern);
+ }
+ }
+
+ /*=================================GeneralLongTimePattern=====================
+ **Property: Return the pattern for 'g' general format: shortDate + Long time
+ **Note: This is used by DateTimeFormat.cs to get the pattern for 'g'
+ ** We put this internal property here so that we can avoid doing the
+ ** concatation every time somebody asks for the general format.
+ ==============================================================================*/
+
+ internal String GeneralLongTimePattern
+ {
+ get
+ {
+ if (generalLongTimePattern == null)
+ {
+ generalLongTimePattern = ShortDatePattern + " " + LongTimePattern;
+ }
+ return (generalLongTimePattern);
+ }
+ }
+
+ /*=================================DateTimeOffsetPattern==========================
+ **Property: Return the default pattern DateTimeOffset : shortDate + long time + time zone offset
+ **Note: This is used by DateTimeFormat.cs to get the pattern for short Date + long time + time zone offset
+ ** We put this internal property here so that we can avoid doing the
+ ** concatation every time somebody uses this form
+ ==============================================================================*/
+
+ /*=================================DateTimeOffsetPattern==========================
+ **Property: Return the default pattern DateTimeOffset : shortDate + long time + time zone offset
+ **Note: This is used by DateTimeFormat.cs to get the pattern for short Date + long time + time zone offset
+ ** We put this internal property here so that we can avoid doing the
+ ** concatation every time somebody uses this form
+ ==============================================================================*/
+
+ internal String DateTimeOffsetPattern
+ {
+ get
+ {
+ if (dateTimeOffsetPattern == null)
+ {
+ string dateTimePattern = ShortDatePattern + " " + LongTimePattern;
+
+ /* LongTimePattern might contain a "z" as part of the format string in which case we don't want to append a time zone offset */
+
+ bool foundZ = false;
+ bool inQuote = false;
+ char quote = '\'';
+ for (int i = 0; !foundZ && i < LongTimePattern.Length; i++)
+ {
+ switch (LongTimePattern[i])
+ {
+ case 'z':
+ /* if we aren't in a quote, we've found a z */
+ foundZ = !inQuote;
+ /* we'll fall out of the loop now because the test includes !foundZ */
+ break;
+ case '\'':
+ case '\"':
+ if (inQuote && (quote == LongTimePattern[i]))
+ {
+ /* we were in a quote and found a matching exit quote, so we are outside a quote now */
+ inQuote = false;
+ }
+ else if (!inQuote)
+ {
+ quote = LongTimePattern[i];
+ inQuote = true;
+ }
+ else
+ {
+ /* we were in a quote and saw the other type of quote character, so we are still in a quote */
+ }
+ break;
+ case '%':
+ case '\\':
+ i++; /* skip next character that is escaped by this backslash */
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!foundZ)
+ {
+ dateTimePattern = dateTimePattern + " zzz";
+ }
+
+ dateTimeOffsetPattern = dateTimePattern;
+ }
+ return (dateTimeOffsetPattern);
+ }
+ }
+
+ // Note that cultureData derives this from the long time format (unless someone's set this previously)
+ // Note that this property is quite undesirable.
+ public string TimeSeparator
+ {
+ get
+ {
+ if (timeSeparator == null)
+ {
+ timeSeparator = _cultureData.TimeSeparator;
+ }
+ Debug.Assert(this.timeSeparator != null, "DateTimeFormatInfo.TimeSeparator, timeSeparator != null");
+ return (timeSeparator);
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value), SR.ArgumentNull_String);
+ }
+
+ Contract.EndContractBlock();
+ ClearTokenHashTable();
+
+ timeSeparator = value;
+ }
+ }
+
+ public String UniversalSortableDateTimePattern
+ {
+ get
+ {
+ return (universalSortableDateTimePattern);
+ }
+ }
+
+ // For our "patterns" arrays we have 2 variables, a string and a string[]
+ //
+ // The string[] contains the list of patterns, EXCEPT the default may not be included.
+ // The string contains the default pattern.
+ // When we initially construct our string[], we set the string to string[0]
+ public String YearMonthPattern
+ {
+ get
+ {
+ // Initialize our year/month pattern from the 1st array value if not set
+ if (this.yearMonthPattern == null)
+ {
+ // Initialize our data
+ this.yearMonthPattern = this.UnclonedYearMonthPatterns[0];
+ }
+ return this.yearMonthPattern;
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_String);
+ }
+ Contract.EndContractBlock();
+
+ // Remember the new string
+ this.yearMonthPattern = value;
+
+ // Clear the token hash table, note that even short times could require this
+ ClearTokenHashTable();
+ }
+ }
+
+ //
+ // Check if a string array contains a null value, and throw ArgumentNullException with parameter name "value"
+ //
+ private static void CheckNullValue(String[] values, int length)
+ {
+ Debug.Assert(values != null, "value != null");
+ Debug.Assert(values.Length >= length);
+ for (int i = 0; i < length; i++)
+ {
+ if (values[i] == null)
+ {
+ throw new ArgumentNullException("value",
+ SR.ArgumentNull_ArrayValue);
+ }
+ }
+ }
+
+
+ public String[] AbbreviatedDayNames
+ {
+ get
+ {
+ return ((String[])internalGetAbbreviatedDayOfWeekNames().Clone());
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_Array);
+ }
+ if (value.Length != 7)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 7), nameof(value));
+ }
+ Contract.EndContractBlock();
+ CheckNullValue(value, value.Length);
+ ClearTokenHashTable();
+
+ abbreviatedDayNames = value;
+ }
+ }
+
+ // Returns the string array of the one-letter day of week names.
+ public String[] ShortestDayNames
+ {
+ get
+ {
+ return ((String[])internalGetSuperShortDayNames().Clone());
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_Array);
+ }
+ if (value.Length != 7)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 7), nameof(value));
+ }
+ Contract.EndContractBlock();
+ CheckNullValue(value, value.Length);
+ this.m_superShortDayNames = value;
+ }
+ }
+
+
+ public String[] DayNames
+ {
+ get
+ {
+ return ((String[])internalGetDayOfWeekNames().Clone());
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_Array);
+ }
+ if (value.Length != 7)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 7), nameof(value));
+ }
+ Contract.EndContractBlock();
+ CheckNullValue(value, value.Length);
+ ClearTokenHashTable();
+
+ dayNames = value;
+ }
+ }
+
+
+ public String[] AbbreviatedMonthNames
+ {
+ get
+ {
+ return ((String[])internalGetAbbreviatedMonthNames().Clone());
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_Array);
+ }
+ if (value.Length != 13)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 13), nameof(value));
+ }
+ Contract.EndContractBlock();
+ CheckNullValue(value, value.Length - 1);
+ ClearTokenHashTable();
+ abbreviatedMonthNames = value;
+ }
+ }
+
+
+ public String[] MonthNames
+ {
+ get
+ {
+ return ((String[])internalGetMonthNames().Clone());
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_Array);
+ }
+ if (value.Length != 13)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 13), nameof(value));
+ }
+ Contract.EndContractBlock();
+ CheckNullValue(value, value.Length - 1);
+ monthNames = value;
+ ClearTokenHashTable();
+ }
+ }
+
+ // Whitespaces that we allow in the month names.
+ // U+00a0 is non-breaking space.
+ private static readonly char[] s_monthSpaces = { ' ', '\u00a0' };
+
+ internal bool HasSpacesInMonthNames
+ {
+ get
+ {
+ return (FormatFlags & DateTimeFormatFlags.UseSpacesInMonthNames) != 0;
+ }
+ }
+
+ internal bool HasSpacesInDayNames
+ {
+ get
+ {
+ return (FormatFlags & DateTimeFormatFlags.UseSpacesInDayNames) != 0;
+ }
+ }
+
+
+ //
+ // internalGetMonthName
+ //
+ // Actions: Return the month name using the specified MonthNameStyles in either abbreviated form
+ // or full form.
+ // Arguments:
+ // month
+ // style To indicate a form like regular/genitive/month name in a leap year.
+ // abbreviated When true, return abbreviated form. Otherwise, return a full form.
+ // Exceptions:
+ // ArgumentOutOfRangeException When month name is invalid.
+ //
+ internal String internalGetMonthName(int month, MonthNameStyles style, bool abbreviated)
+ {
+ //
+ // Right now, style is mutual exclusive, but I make the style to be flag so that
+ // maybe we can combine flag if there is such a need.
+ //
+ String[] monthNamesArray = null;
+ switch (style)
+ {
+ case MonthNameStyles.Genitive:
+ monthNamesArray = internalGetGenitiveMonthNames(abbreviated);
+ break;
+ case MonthNameStyles.LeapYear:
+ monthNamesArray = internalGetLeapYearMonthNames(/*abbreviated*/);
+ break;
+ default:
+ monthNamesArray = (abbreviated ? internalGetAbbreviatedMonthNames() : internalGetMonthNames());
+ break;
+ }
+ // The month range is from 1 ~ this.m_monthNames.Length
+ // (actually is 13 right now for all cases)
+ if ((month < 1) || (month > monthNamesArray.Length))
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(month), SR.Format(SR.ArgumentOutOfRange_Range,
+ 1, monthNamesArray.Length));
+ }
+ return (monthNamesArray[month - 1]);
+ }
+
+ //
+ // internalGetGenitiveMonthNames
+ //
+ // Action: Retrieve the array which contains the month names in genitive form.
+ // If this culture does not use the gentive form, the normal month name is returned.
+ // Arguments:
+ // abbreviated When true, return abbreviated form. Otherwise, return a full form.
+ //
+ private String[] internalGetGenitiveMonthNames(bool abbreviated)
+ {
+ if (abbreviated)
+ {
+ if (this.m_genitiveAbbreviatedMonthNames == null)
+ {
+ this.m_genitiveAbbreviatedMonthNames = _cultureData.AbbreviatedGenitiveMonthNames(this.Calendar.ID);
+ Debug.Assert(this.m_genitiveAbbreviatedMonthNames.Length == 13,
+ "[DateTimeFormatInfo.GetGenitiveMonthNames] Expected 13 abbreviated genitive month names in a year");
+ }
+ return (this.m_genitiveAbbreviatedMonthNames);
+ }
+
+ if (this.genitiveMonthNames == null)
+ {
+ this.genitiveMonthNames = _cultureData.GenitiveMonthNames(this.Calendar.ID);
+ Debug.Assert(this.genitiveMonthNames.Length == 13,
+ "[DateTimeFormatInfo.GetGenitiveMonthNames] Expected 13 genitive month names in a year");
+ }
+ return (this.genitiveMonthNames);
+ }
+
+ //
+ // internalGetLeapYearMonthNames
+ //
+ // Actions: Retrieve the month names used in a leap year.
+ // If this culture does not have different month names in a leap year, the normal month name is returned.
+ // Agruments: None. (can use abbreviated later if needed)
+ //
+ internal String[] internalGetLeapYearMonthNames(/*bool abbreviated*/)
+ {
+ if (this.leapYearMonthNames == null)
+ {
+ Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.internalGetLeapYearMonthNames] Expected Calendar.ID > 0");
+ this.leapYearMonthNames = _cultureData.LeapYearMonthNames(Calendar.ID);
+ Debug.Assert(this.leapYearMonthNames.Length == 13,
+ "[DateTimeFormatInfo.internalGetLeapYearMonthNames] Expepcted 13 leap year month names");
+ }
+ return (leapYearMonthNames);
+ }
+
+
+ public String GetAbbreviatedDayName(DayOfWeek dayofweek)
+ {
+ if ((int)dayofweek < 0 || (int)dayofweek > 6)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(dayofweek), SR.Format(SR.ArgumentOutOfRange_Range,
+ DayOfWeek.Sunday, DayOfWeek.Saturday));
+ }
+ Contract.EndContractBlock();
+ //
+ // Don't call the public property AbbreviatedDayNames here since a clone is needed in that
+ // property, so it will be slower. Instead, use GetAbbreviatedDayOfWeekNames() directly.
+ //
+ return (internalGetAbbreviatedDayOfWeekNames()[(int)dayofweek]);
+ }
+
+ // Returns the super short day of week names for the specified day of week.
+ public string GetShortestDayName(DayOfWeek dayOfWeek)
+ {
+ if ((int)dayOfWeek < 0 || (int)dayOfWeek > 6)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(dayOfWeek), SR.Format(SR.ArgumentOutOfRange_Range,
+ DayOfWeek.Sunday, DayOfWeek.Saturday));
+ }
+ Contract.EndContractBlock();
+ //
+ // Don't call the public property SuperShortDayNames here since a clone is needed in that
+ // property, so it will be slower. Instead, use internalGetSuperShortDayNames() directly.
+ //
+ return (internalGetSuperShortDayNames()[(int)dayOfWeek]);
+ }
+
+ // Get all possible combination of inputs
+ private static String[] GetCombinedPatterns(String[] patterns1, String[] patterns2, String connectString)
+ {
+ Debug.Assert(patterns1 != null);
+ Debug.Assert(patterns2 != null);
+
+ // Get array size
+ String[] result = new String[patterns1.Length * patterns2.Length];
+
+ // Counter of actual results
+ int k = 0;
+ for (int i = 0; i < patterns1.Length; i++)
+ {
+ for (int j = 0; j < patterns2.Length; j++)
+ {
+ // Can't combine if null or empty
+ result[k++] = patterns1[i] + connectString + patterns2[j];
+ }
+ }
+
+ // Return the combinations
+ return (result);
+ }
+
+ public string[] GetAllDateTimePatterns()
+ {
+ List<String> results = new List<String>(DEFAULT_ALL_DATETIMES_SIZE);
+
+ for (int i = 0; i < DateTimeFormat.allStandardFormats.Length; i++)
+ {
+ String[] strings = GetAllDateTimePatterns(DateTimeFormat.allStandardFormats[i]);
+ for (int j = 0; j < strings.Length; j++)
+ {
+ results.Add(strings[j]);
+ }
+ }
+ return results.ToArray();
+ }
+
+ public string[] GetAllDateTimePatterns(char format)
+ {
+ Contract.Ensures(Contract.Result<String[]>() != null);
+ String[] result = null;
+
+ switch (format)
+ {
+ case 'd':
+ result = this.AllShortDatePatterns;
+ break;
+ case 'D':
+ result = this.AllLongDatePatterns;
+ break;
+ case 'f':
+ result = GetCombinedPatterns(AllLongDatePatterns, AllShortTimePatterns, " ");
+ break;
+ case 'F':
+ case 'U':
+ result = GetCombinedPatterns(AllLongDatePatterns, AllLongTimePatterns, " ");
+ break;
+ case 'g':
+ result = GetCombinedPatterns(AllShortDatePatterns, AllShortTimePatterns, " ");
+ break;
+ case 'G':
+ result = GetCombinedPatterns(AllShortDatePatterns, AllLongTimePatterns, " ");
+ break;
+ case 'm':
+ case 'M':
+ result = new String[] { MonthDayPattern };
+ break;
+ case 'o':
+ case 'O':
+ result = new String[] { RoundtripFormat };
+ break;
+ case 'r':
+ case 'R':
+ result = new String[] { rfc1123Pattern };
+ break;
+ case 's':
+ result = new String[] { sortableDateTimePattern };
+ break;
+ case 't':
+ result = this.AllShortTimePatterns;
+ break;
+ case 'T':
+ result = this.AllLongTimePatterns;
+ break;
+ case 'u':
+ result = new String[] { UniversalSortableDateTimePattern };
+ break;
+ case 'y':
+ case 'Y':
+ result = this.AllYearMonthPatterns;
+ break;
+ default:
+ throw new ArgumentException(SR.Format_BadFormatSpecifier, nameof(format));
+ }
+ return (result);
+ }
+
+
+ public String GetDayName(DayOfWeek dayofweek)
+ {
+ if ((int)dayofweek < 0 || (int)dayofweek > 6)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(dayofweek), SR.Format(SR.ArgumentOutOfRange_Range,
+ DayOfWeek.Sunday, DayOfWeek.Saturday));
+ }
+ Contract.EndContractBlock();
+
+ // Use the internal one so that we don't clone the array unnecessarily
+ return (internalGetDayOfWeekNames()[(int)dayofweek]);
+ }
+
+
+
+ public String GetAbbreviatedMonthName(int month)
+ {
+ if (month < 1 || month > 13)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(month), SR.Format(SR.ArgumentOutOfRange_Range,
+ 1, 13));
+ }
+ Contract.EndContractBlock();
+ // Use the internal one so we don't clone the array unnecessarily
+ return (internalGetAbbreviatedMonthNames()[month - 1]);
+ }
+
+
+ public String GetMonthName(int month)
+ {
+ if (month < 1 || month > 13)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(month), SR.Format(SR.ArgumentOutOfRange_Range,
+ 1, 13));
+ }
+ Contract.EndContractBlock();
+ // Use the internal one so we don't clone the array unnecessarily
+ return (internalGetMonthNames()[month - 1]);
+ }
+
+ // For our "patterns" arrays we have 2 variables, a string and a string[]
+ //
+ // The string[] contains the list of patterns, EXCEPT the default may not be included.
+ // The string contains the default pattern.
+ // When we initially construct our string[], we set the string to string[0]
+ //
+ // The resulting [] can get returned to the calling app, so clone it.
+ private static string[] GetMergedPatterns(string[] patterns, string defaultPattern)
+ {
+ Debug.Assert(patterns != null && patterns.Length > 0,
+ "[DateTimeFormatInfo.GetMergedPatterns]Expected array of at least one pattern");
+ Debug.Assert(defaultPattern != null,
+ "[DateTimeFormatInfo.GetMergedPatterns]Expected non null default string");
+
+ // If the default happens to be the first in the list just return (a cloned) copy
+ if (defaultPattern == patterns[0])
+ {
+ return (string[])patterns.Clone();
+ }
+
+ // We either need a bigger list, or the pattern from the list.
+ int i;
+ for (i = 0; i < patterns.Length; i++)
+ {
+ // Stop if we found it
+ if (defaultPattern == patterns[i])
+ break;
+ }
+
+ // Either way we're going to need a new array
+ string[] newPatterns;
+
+ // Did we find it
+ if (i < patterns.Length)
+ {
+ // Found it, output will be same size
+ newPatterns = (string[])patterns.Clone();
+
+ // Have to move [0] item to [i] so we can re-write default at [0]
+ // (remember defaultPattern == [i] so this is OK)
+ newPatterns[i] = newPatterns[0];
+ }
+ else
+ {
+ // Not found, make room for it
+ newPatterns = new String[patterns.Length + 1];
+
+ // Copy existing array
+ Array.Copy(patterns, 0, newPatterns, 1, patterns.Length);
+ }
+
+ // Remember the default
+ newPatterns[0] = defaultPattern;
+
+ // Return the reconstructed list
+ return newPatterns;
+ }
+
+ // Needed by DateTimeFormatInfo and DateTimeFormat
+ internal const String RoundtripFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK";
+ internal const String RoundtripDateTimeUnfixed = "yyyy'-'MM'-'ddTHH':'mm':'ss zzz";
+
+ // Default string isn't necessarily in our string array, so get the
+ // merged patterns of both
+ private String[] AllYearMonthPatterns
+ {
+ get
+ {
+ return GetMergedPatterns(this.UnclonedYearMonthPatterns, this.YearMonthPattern);
+ }
+ }
+
+ private String[] AllShortDatePatterns
+ {
+ get
+ {
+ return GetMergedPatterns(this.UnclonedShortDatePatterns, this.ShortDatePattern);
+ }
+ }
+
+ private String[] AllShortTimePatterns
+ {
+ get
+ {
+ return GetMergedPatterns(this.UnclonedShortTimePatterns, this.ShortTimePattern);
+ }
+ }
+
+ private String[] AllLongDatePatterns
+ {
+ get
+ {
+ return GetMergedPatterns(this.UnclonedLongDatePatterns, this.LongDatePattern);
+ }
+ }
+
+ private String[] AllLongTimePatterns
+ {
+ get
+ {
+ return GetMergedPatterns(this.UnclonedLongTimePatterns, this.LongTimePattern);
+ }
+ }
+
+ // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy.
+ // This won't include default, call AllYearMonthPatterns
+ private String[] UnclonedYearMonthPatterns
+ {
+ get
+ {
+ if (allYearMonthPatterns == null)
+ {
+ Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.UnclonedYearMonthPatterns] Expected Calendar.ID > 0");
+ this.allYearMonthPatterns = _cultureData.YearMonths(this.Calendar.ID);
+ Debug.Assert(this.allYearMonthPatterns.Length > 0,
+ "[DateTimeFormatInfo.UnclonedYearMonthPatterns] Expected some year month patterns");
+ }
+
+ return allYearMonthPatterns;
+ }
+ }
+
+
+ // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy.
+ // This won't include default, call AllShortDatePatterns
+ private String[] UnclonedShortDatePatterns
+ {
+ get
+ {
+ if (allShortDatePatterns == null)
+ {
+ Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.UnclonedShortDatePatterns] Expected Calendar.ID > 0");
+ this.allShortDatePatterns = _cultureData.ShortDates(this.Calendar.ID);
+ Debug.Assert(this.allShortDatePatterns.Length > 0,
+ "[DateTimeFormatInfo.UnclonedShortDatePatterns] Expected some short date patterns");
+ }
+
+ return this.allShortDatePatterns;
+ }
+ }
+
+ // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy.
+ // This won't include default, call AllLongDatePatterns
+ private String[] UnclonedLongDatePatterns
+ {
+ get
+ {
+ if (allLongDatePatterns == null)
+ {
+ Debug.Assert(Calendar.ID > 0, "[DateTimeFormatInfo.UnclonedLongDatePatterns] Expected Calendar.ID > 0");
+ this.allLongDatePatterns = _cultureData.LongDates(this.Calendar.ID);
+ Debug.Assert(this.allLongDatePatterns.Length > 0,
+ "[DateTimeFormatInfo.UnclonedLongDatePatterns] Expected some long date patterns");
+ }
+
+ return this.allLongDatePatterns;
+ }
+ }
+
+ // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy.
+ // This won't include default, call AllShortTimePatterns
+ private String[] UnclonedShortTimePatterns
+ {
+ get
+ {
+ if (this.allShortTimePatterns == null)
+ {
+ this.allShortTimePatterns = _cultureData.ShortTimes;
+ Debug.Assert(this.allShortTimePatterns.Length > 0,
+ "[DateTimeFormatInfo.UnclonedShortTimePatterns] Expected some short time patterns");
+ }
+
+ return this.allShortTimePatterns;
+ }
+ }
+
+ // NOTE: Clone this string array if you want to return it to user. Otherwise, you are returning a writable cache copy.
+ // This won't include default, call AllLongTimePatterns
+ private String[] UnclonedLongTimePatterns
+ {
+ get
+ {
+ if (this.allLongTimePatterns == null)
+ {
+ this.allLongTimePatterns = _cultureData.LongTimes;
+ Debug.Assert(this.allLongTimePatterns.Length > 0,
+ "[DateTimeFormatInfo.UnclonedLongTimePatterns] Expected some long time patterns");
+ }
+
+ return this.allLongTimePatterns;
+ }
+ }
+
+ public static DateTimeFormatInfo ReadOnly(DateTimeFormatInfo dtfi)
+ {
+ if (dtfi == null)
+ {
+ throw new ArgumentNullException(nameof(dtfi),
+ SR.ArgumentNull_Obj);
+ }
+ Contract.EndContractBlock();
+ if (dtfi.IsReadOnly)
+ {
+ return (dtfi);
+ }
+ DateTimeFormatInfo newInfo = (DateTimeFormatInfo)(dtfi.MemberwiseClone());
+ // We can use the data member calendar in the setter, instead of the property Calendar,
+ // since the cloned copy should have the same state as the original copy.
+ newInfo.calendar = Calendar.ReadOnly(dtfi.Calendar);
+ newInfo._isReadOnly = true;
+ return (newInfo);
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return (_isReadOnly);
+ }
+ }
+
+ // Return the native name for the calendar in DTFI.Calendar. The native name is referred to
+ // the culture used to create the DTFI. E.g. in the following example, the native language is Japanese.
+ // DateTimeFormatInfo dtfi = new CultureInfo("ja-JP", false).DateTimeFormat.Calendar = new JapaneseCalendar();
+ // String nativeName = dtfi.NativeCalendarName; // Get the Japanese name for the Japanese calendar.
+ // DateTimeFormatInfo dtfi = new CultureInfo("ja-JP", false).DateTimeFormat.Calendar = new GregorianCalendar(GregorianCalendarTypes.Localized);
+ // String nativeName = dtfi.NativeCalendarName; // Get the Japanese name for the Gregorian calendar.
+ public string NativeCalendarName
+ {
+ get
+ {
+ return _cultureData.CalendarName(Calendar.ID);
+ }
+ }
+
+ //
+ // Used by custom cultures and others to set the list of available formats. Note that none of them are
+ // explicitly used unless someone calls GetAllDateTimePatterns and subsequently uses one of the items
+ // from the list.
+ //
+ // Most of the format characters that can be used in GetAllDateTimePatterns are
+ // not really needed since they are one of the following:
+ //
+ // r/R/s/u locale-independent constants -- cannot be changed!
+ // m/M/y/Y fields with a single string in them -- that can be set through props directly
+ // f/F/g/G/U derived fields based on combinations of various of the below formats
+ //
+ // NOTE: No special validation is done here beyond what is done when the actual respective fields
+ // are used (what would be the point of disallowing here what we allow in the appropriate property?)
+ //
+ // WARNING: If more validation is ever done in one place, it should be done in the other.
+ //
+ public void SetAllDateTimePatterns(String[] patterns, char format)
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+
+ if (patterns == null)
+ {
+ throw new ArgumentNullException(nameof(patterns), SR.ArgumentNull_Array);
+ }
+
+ if (patterns.Length == 0)
+ {
+ throw new ArgumentException(SR.Arg_ArrayZeroError, nameof(patterns));
+ }
+
+ Contract.EndContractBlock();
+
+ for (int i = 0; i < patterns.Length; i++)
+ {
+ if (patterns[i] == null)
+ {
+ throw new ArgumentNullException("patterns[" + i + "]", SR.ArgumentNull_ArrayValue);
+ }
+ }
+
+ // Remember the patterns, and use the 1st as default
+ switch (format)
+ {
+ case 'd':
+ allShortDatePatterns = patterns;
+ shortDatePattern = allShortDatePatterns[0];
+ break;
+
+ case 'D':
+ allLongDatePatterns = patterns;
+ longDatePattern = allLongDatePatterns[0];
+ break;
+
+ case 't':
+ allShortTimePatterns = patterns;
+ shortTimePattern = allShortTimePatterns[0];
+ break;
+
+ case 'T':
+ allLongTimePatterns = patterns;
+ longTimePattern = allLongTimePatterns[0];
+ break;
+
+ case 'y':
+ case 'Y':
+ allYearMonthPatterns = patterns;
+ yearMonthPattern = allYearMonthPatterns[0];
+ break;
+
+ default:
+ throw new ArgumentException(SR.Format_BadFormatSpecifier, nameof(format));
+ }
+
+ // Clear the token hash table, note that even short dates could require this
+ ClearTokenHashTable();
+ }
+
+ public String[] AbbreviatedMonthGenitiveNames
+ {
+ get
+ {
+ return ((String[])internalGetGenitiveMonthNames(true).Clone());
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_Array);
+ }
+ if (value.Length != 13)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 13), nameof(value));
+ }
+ Contract.EndContractBlock();
+ CheckNullValue(value, value.Length - 1);
+ ClearTokenHashTable();
+ this.m_genitiveAbbreviatedMonthNames = value;
+ }
+ }
+
+ public String[] MonthGenitiveNames
+ {
+ get
+ {
+ return ((String[])internalGetGenitiveMonthNames(false).Clone());
+ }
+
+ set
+ {
+ if (IsReadOnly)
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value),
+ SR.ArgumentNull_Array);
+ }
+ if (value.Length != 13)
+ {
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidArrayLength, 13), nameof(value));
+ }
+ Contract.EndContractBlock();
+ CheckNullValue(value, value.Length - 1);
+ genitiveMonthNames = value;
+ ClearTokenHashTable();
+ }
+ }
+
+ //
+ // Positive TimeSpan Pattern
+ //
+ [NonSerialized]
+ private string _fullTimeSpanPositivePattern;
+ internal String FullTimeSpanPositivePattern
+ {
+ get
+ {
+ 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";
+ }
+ return _fullTimeSpanPositivePattern;
+ }
+ }
+
+ //
+ // Negative TimeSpan Pattern
+ //
+ [NonSerialized]
+ private string _fullTimeSpanNegativePattern;
+ internal String FullTimeSpanNegativePattern
+ {
+ get
+ {
+ if (_fullTimeSpanNegativePattern == null)
+ _fullTimeSpanNegativePattern = "'-'" + FullTimeSpanPositivePattern;
+ return _fullTimeSpanNegativePattern;
+ }
+ }
+
+ //
+ // Get suitable CompareInfo from current DTFI object.
+ //
+ internal CompareInfo CompareInfo
+ {
+ get
+ {
+ if (_compareInfo == null)
+ {
+ // We use the regular GetCompareInfo here to make sure the created CompareInfo object is stored in the
+ // CompareInfo cache. otherwise we would just create CompareInfo using _cultureData.
+ _compareInfo = CompareInfo.GetCompareInfo(_cultureData.SCOMPAREINFO);
+ }
+
+ return _compareInfo;
+ }
+ }
+
+
+ internal const DateTimeStyles InvalidDateTimeStyles = ~(DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite
+ | DateTimeStyles.AllowInnerWhite | DateTimeStyles.NoCurrentDateDefault
+ | DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeLocal
+ | DateTimeStyles.AssumeUniversal | DateTimeStyles.RoundtripKind);
+
+ internal static void ValidateStyles(DateTimeStyles style, String parameterName)
+ {
+ if ((style & InvalidDateTimeStyles) != 0)
+ {
+ throw new ArgumentException(SR.Argument_InvalidDateTimeStyles, parameterName);
+ }
+ if (((style & (DateTimeStyles.AssumeLocal)) != 0) && ((style & (DateTimeStyles.AssumeUniversal)) != 0))
+ {
+ throw new ArgumentException(SR.Argument_ConflictingDateTimeStyles, parameterName);
+ }
+ Contract.EndContractBlock();
+ if (((style & DateTimeStyles.RoundtripKind) != 0)
+ && ((style & (DateTimeStyles.AssumeLocal | DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)) != 0))
+ {
+ throw new ArgumentException(SR.Argument_ConflictingDateTimeRoundtripStyles, parameterName);
+ }
+ }
+
+ //
+ // Actions: Return the internal flag used in formatting and parsing.
+ // The flag can be used to indicate things like if genitive forms is used in this DTFi, or if leap year gets different month names.
+ //
+ internal DateTimeFormatFlags FormatFlags
+ {
+ get
+ {
+ if (formatFlags == DateTimeFormatFlags.NotInitialized)
+ {
+ // Build the format flags from the data in this DTFI
+ formatFlags = DateTimeFormatFlags.None;
+ formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagGenitiveMonth(
+ MonthNames, internalGetGenitiveMonthNames(false), AbbreviatedMonthNames, internalGetGenitiveMonthNames(true));
+ formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseSpaceInMonthNames(
+ MonthNames, internalGetGenitiveMonthNames(false), AbbreviatedMonthNames, internalGetGenitiveMonthNames(true));
+ formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseSpaceInDayNames(DayNames, AbbreviatedDayNames);
+ formatFlags |= (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseHebrewCalendar((int)Calendar.ID);
+ }
+ return (formatFlags);
+ }
+ }
+
+ internal Boolean HasForceTwoDigitYears
+ {
+ get
+ {
+ switch (calendar.ID)
+ {
+ // Handle Japanese and Taiwan cases.
+ // If is y/yy, do not get (year % 100). "y" will print
+ // year without leading zero. "yy" will print year with two-digit in leading zero.
+ // If pattern is yyy/yyyy/..., print year value with two-digit in leading zero.
+ // So year 5 is "05", and year 125 is "125".
+ // The reason for not doing (year % 100) is for Taiwan calendar.
+ // If year 125, then output 125 and not 25.
+ // Note: OS uses "yyyy" for Taiwan calendar by default.
+ case (CalendarId.JAPAN):
+ case (CalendarId.TAIWAN):
+ return true;
+ }
+ return false;
+ }
+ }
+
+ // Returns whether the YearMonthAdjustment function has any fix-up work to do for this culture/calendar.
+ internal Boolean HasYearMonthAdjustment
+ {
+ get
+ {
+ return ((FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0);
+ }
+ }
+
+ // This is a callback that the parser can make back into the DTFI to let it fiddle with special
+ // cases associated with that culture or calendar. Currently this only has special cases for
+ // the Hebrew calendar, but this could be extended to other cultures.
+ //
+ // The return value is whether the year and month are actually valid for this calendar.
+ internal Boolean YearMonthAdjustment(ref int year, ref int month, Boolean parsedMonthName)
+ {
+ if ((FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0)
+ {
+ // Special rules to fix up the Hebrew year/month
+
+ // When formatting, we only format up to the hundred digit of the Hebrew year, although Hebrew year is now over 5000.
+ // E.g. if the year is 5763, we only format as 763.
+ if (year < 1000)
+ {
+ year += 5000;
+ }
+
+ // Because we need to calculate leap year, we should fall out now for an invalid year.
+ if (year < Calendar.GetYear(Calendar.MinSupportedDateTime) || year > Calendar.GetYear(Calendar.MaxSupportedDateTime))
+ {
+ return false;
+ }
+
+ // To handle leap months, the set of month names in the symbol table does not always correspond to the numbers.
+ // For non-leap years, month 7 (Adar Bet) is not present, so we need to make using this month invalid and
+ // shuffle the other months down.
+ if (parsedMonthName)
+ {
+ if (!Calendar.IsLeapYear(year))
+ {
+ if (month >= 8)
+ {
+ month--;
+ }
+ else if (month == 7)
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ //
+ // DateTimeFormatInfo tokenizer. This is used by DateTime.Parse() to break input string into tokens.
+ //
+ [NonSerialized]
+ private TokenHashValue[] _dtfiTokenHash;
+
+ private const int TOKEN_HASH_SIZE = 199;
+ private const int SECOND_PRIME = 197;
+ private const String dateSeparatorOrTimeZoneOffset = "-";
+ private const String invariantDateSeparator = "/";
+ private const String invariantTimeSeparator = ":";
+
+ //
+ // Common Ignorable Symbols
+ //
+ internal const String IgnorablePeriod = ".";
+ internal const String IgnorableComma = ",";
+
+ //
+ // Year/Month/Day suffixes
+ //
+ internal const String CJKYearSuff = "\u5e74";
+ internal const String CJKMonthSuff = "\u6708";
+ internal const String CJKDaySuff = "\u65e5";
+
+ internal const String KoreanYearSuff = "\ub144";
+ internal const String KoreanMonthSuff = "\uc6d4";
+ internal const String KoreanDaySuff = "\uc77c";
+
+ internal const String KoreanHourSuff = "\uc2dc";
+ internal const String KoreanMinuteSuff = "\ubd84";
+ internal const String KoreanSecondSuff = "\ucd08";
+
+ internal const String CJKHourSuff = "\u6642";
+ internal const String ChineseHourSuff = "\u65f6";
+
+ internal const String CJKMinuteSuff = "\u5206";
+ internal const String CJKSecondSuff = "\u79d2";
+
+ internal const String LocalTimeMark = "T";
+
+ internal const String GMTName = "GMT";
+ internal const String ZuluName = "Z";
+
+ internal const String KoreanLangName = "ko";
+ internal const String JapaneseLangName = "ja";
+ internal const String EnglishLangName = "en";
+
+ private static volatile DateTimeFormatInfo s_jajpDTFI;
+ private static volatile DateTimeFormatInfo s_zhtwDTFI;
+
+ //
+ // Create a Japanese DTFI which uses JapaneseCalendar. This is used to parse
+ // date string with Japanese era name correctly even when the supplied DTFI
+ // does not use Japanese calendar.
+ // The created instance is stored in global s_jajpDTFI.
+ //
+ internal static DateTimeFormatInfo GetJapaneseCalendarDTFI()
+ {
+ DateTimeFormatInfo temp = s_jajpDTFI;
+ if (temp == null)
+ {
+ temp = new CultureInfo("ja-JP", false).DateTimeFormat;
+ temp.Calendar = JapaneseCalendar.GetDefaultInstance();
+ s_jajpDTFI = temp;
+ }
+ return (temp);
+ }
+
+ // Create a Taiwan DTFI which uses TaiwanCalendar. This is used to parse
+ // date string with era name correctly even when the supplied DTFI
+ // does not use Taiwan calendar.
+ // The created instance is stored in global s_zhtwDTFI.
+ internal static DateTimeFormatInfo GetTaiwanCalendarDTFI()
+ {
+ DateTimeFormatInfo temp = s_zhtwDTFI;
+ if (temp == null)
+ {
+ temp = new CultureInfo("zh-TW", false).DateTimeFormat;
+ temp.Calendar = TaiwanCalendar.GetDefaultInstance();
+ s_zhtwDTFI = temp;
+ }
+ return (temp);
+ }
+
+
+ // DTFI properties should call this when the setter are called.
+ private void ClearTokenHashTable()
+ {
+ _dtfiTokenHash = null;
+ formatFlags = DateTimeFormatFlags.NotInitialized;
+ }
+
+ internal TokenHashValue[] CreateTokenHashTable()
+ {
+ TokenHashValue[] temp = _dtfiTokenHash;
+ if (temp == null)
+ {
+ temp = new TokenHashValue[TOKEN_HASH_SIZE];
+
+ bool koreanLanguage = LanguageName.Equals(KoreanLangName);
+
+ string sep = this.TimeSeparator.Trim();
+ if (IgnorableComma != sep) InsertHash(temp, IgnorableComma, TokenType.IgnorableSymbol, 0);
+ if (IgnorablePeriod != sep) InsertHash(temp, IgnorablePeriod, TokenType.IgnorableSymbol, 0);
+
+ if (KoreanHourSuff != sep && CJKHourSuff != sep && ChineseHourSuff != sep)
+ {
+ //
+ // On the Macintosh, the default TimeSeparator is identical to the KoreanHourSuff, CJKHourSuff, or ChineseHourSuff for some cultures like
+ // ja-JP and ko-KR. In these cases having the same symbol inserted into the hash table with multiple TokenTypes causes undesirable
+ // DateTime.Parse behavior. For instance, the DateTimeFormatInfo.Tokenize() method might return SEP_DateOrOffset for KoreanHourSuff
+ // instead of SEP_HourSuff.
+ //
+ InsertHash(temp, this.TimeSeparator, TokenType.SEP_Time, 0);
+ }
+
+ InsertHash(temp, this.AMDesignator, TokenType.SEP_Am | TokenType.Am, 0);
+ InsertHash(temp, this.PMDesignator, TokenType.SEP_Pm | TokenType.Pm, 1);
+
+ // TODO: This ignores similar custom cultures
+ if (LanguageName.Equals("sq"))
+ {
+ // Albanian allows time formats like "12:00.PD"
+ InsertHash(temp, IgnorablePeriod + this.AMDesignator, TokenType.SEP_Am | TokenType.Am, 0);
+ InsertHash(temp, IgnorablePeriod + this.PMDesignator, TokenType.SEP_Pm | TokenType.Pm, 1);
+ }
+
+ // CJK suffix
+ InsertHash(temp, CJKYearSuff, TokenType.SEP_YearSuff, 0);
+ InsertHash(temp, KoreanYearSuff, TokenType.SEP_YearSuff, 0);
+ InsertHash(temp, CJKMonthSuff, TokenType.SEP_MonthSuff, 0);
+ InsertHash(temp, KoreanMonthSuff, TokenType.SEP_MonthSuff, 0);
+ InsertHash(temp, CJKDaySuff, TokenType.SEP_DaySuff, 0);
+ InsertHash(temp, KoreanDaySuff, TokenType.SEP_DaySuff, 0);
+
+ InsertHash(temp, CJKHourSuff, TokenType.SEP_HourSuff, 0);
+ InsertHash(temp, ChineseHourSuff, TokenType.SEP_HourSuff, 0);
+ InsertHash(temp, CJKMinuteSuff, TokenType.SEP_MinuteSuff, 0);
+ InsertHash(temp, CJKSecondSuff, TokenType.SEP_SecondSuff, 0);
+
+ // TODO: This ignores other custom cultures that might want to do something similar
+ if (koreanLanguage)
+ {
+ // Korean suffix
+ InsertHash(temp, KoreanHourSuff, TokenType.SEP_HourSuff, 0);
+ InsertHash(temp, KoreanMinuteSuff, TokenType.SEP_MinuteSuff, 0);
+ InsertHash(temp, KoreanSecondSuff, TokenType.SEP_SecondSuff, 0);
+ }
+
+ if (LanguageName.Equals("ky"))
+ {
+ // For some cultures, the date separator works more like a comma, being allowed before or after any date part
+ InsertHash(temp, dateSeparatorOrTimeZoneOffset, TokenType.IgnorableSymbol, 0);
+ }
+ else
+ {
+ InsertHash(temp, dateSeparatorOrTimeZoneOffset, TokenType.SEP_DateOrOffset, 0);
+ }
+
+ String[] dateWords = null;
+ DateTimeFormatInfoScanner scanner = null;
+
+ // We need to rescan the date words since we're always synthetic
+ scanner = new DateTimeFormatInfoScanner();
+ m_dateWords = dateWords = scanner.GetDateWordsOfDTFI(this);
+ // Ensure the formatflags is initialized.
+ DateTimeFormatFlags flag = FormatFlags;
+
+ // For some cultures, the date separator works more like a comma, being allowed before or after any date part.
+ // In these cultures, we do not use normal date separator since we disallow date separator after a date terminal state.
+ // This is determined in DateTimeFormatInfoScanner. Use this flag to determine if we should treat date separator as ignorable symbol.
+ bool useDateSepAsIgnorableSymbol = false;
+
+ String monthPostfix = null;
+ if (dateWords != null)
+ {
+ // There are DateWords. It could be a real date word (such as "de"), or a monthPostfix.
+ // The monthPostfix starts with '\xfffe' (MonthPostfixChar), followed by the real monthPostfix.
+ for (int i = 0; i < dateWords.Length; i++)
+ {
+ switch (dateWords[i][0])
+ {
+ // This is a month postfix
+ case DateTimeFormatInfoScanner.MonthPostfixChar:
+ // Get the real month postfix.
+ monthPostfix = dateWords[i].Substring(1);
+ // Add the month name + postfix into the token.
+ AddMonthNames(temp, monthPostfix);
+ break;
+ case DateTimeFormatInfoScanner.IgnorableSymbolChar:
+ String symbol = dateWords[i].Substring(1);
+ InsertHash(temp, symbol, TokenType.IgnorableSymbol, 0);
+ if (this.DateSeparator.Trim(null).Equals(symbol))
+ {
+ // The date separator is the same as the ignorable symbol.
+ useDateSepAsIgnorableSymbol = true;
+ }
+ break;
+ default:
+ InsertHash(temp, dateWords[i], TokenType.DateWordToken, 0);
+ // TODO: This ignores similar custom cultures
+ if (LanguageName.Equals("eu"))
+ {
+ // Basque has date words with leading dots
+ InsertHash(temp, IgnorablePeriod + dateWords[i], TokenType.DateWordToken, 0);
+ }
+ break;
+ }
+ }
+ }
+
+ if (!useDateSepAsIgnorableSymbol)
+ {
+ // Use the normal date separator.
+ InsertHash(temp, this.DateSeparator, TokenType.SEP_Date, 0);
+ }
+ // Add the regular month names.
+ AddMonthNames(temp, null);
+
+ // Add the abbreviated month names.
+ for (int i = 1; i <= 13; i++)
+ {
+ InsertHash(temp, GetAbbreviatedMonthName(i), TokenType.MonthToken, i);
+ }
+
+
+ if ((FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0)
+ {
+ for (int i = 1; i <= 13; i++)
+ {
+ String str;
+ str = internalGetMonthName(i, MonthNameStyles.Genitive, false);
+ InsertHash(temp, str, TokenType.MonthToken, i);
+ }
+ }
+
+ if ((FormatFlags & DateTimeFormatFlags.UseLeapYearMonth) != 0)
+ {
+ for (int i = 1; i <= 13; i++)
+ {
+ String str;
+ str = internalGetMonthName(i, MonthNameStyles.LeapYear, false);
+ InsertHash(temp, str, TokenType.MonthToken, i);
+ }
+ }
+
+ for (int i = 0; i < 7; i++)
+ {
+ //String str = GetDayOfWeekNames()[i];
+ // We have to call public methods here to work with inherited DTFI.
+ String str = GetDayName((DayOfWeek)i);
+ InsertHash(temp, str, TokenType.DayOfWeekToken, i);
+
+ str = GetAbbreviatedDayName((DayOfWeek)i);
+ InsertHash(temp, str, TokenType.DayOfWeekToken, i);
+ }
+
+ int[] eras = calendar.Eras;
+ for (int i = 1; i <= eras.Length; i++)
+ {
+ InsertHash(temp, GetEraName(i), TokenType.EraToken, i);
+ InsertHash(temp, GetAbbreviatedEraName(i), TokenType.EraToken, i);
+ }
+
+ // TODO: This ignores other cultures that might want to do something similar
+ if (LanguageName.Equals(JapaneseLangName))
+ {
+ // Japanese allows day of week forms like: "(Tue)"
+ for (int i = 0; i < 7; i++)
+ {
+ String specialDayOfWeek = "(" + GetAbbreviatedDayName((DayOfWeek)i) + ")";
+ InsertHash(temp, specialDayOfWeek, TokenType.DayOfWeekToken, i);
+ }
+ if (this.Calendar.GetType() != typeof(JapaneseCalendar))
+ {
+ // Special case for Japanese. If this is a Japanese DTFI, and the calendar is not Japanese calendar,
+ // we will check Japanese Era name as well when the calendar is Gregorian.
+ DateTimeFormatInfo jaDtfi = GetJapaneseCalendarDTFI();
+ for (int i = 1; i <= jaDtfi.Calendar.Eras.Length; i++)
+ {
+ InsertHash(temp, jaDtfi.GetEraName(i), TokenType.JapaneseEraToken, i);
+ InsertHash(temp, jaDtfi.GetAbbreviatedEraName(i), TokenType.JapaneseEraToken, i);
+ // m_abbrevEnglishEraNames[0] contains the name for era 1, so the token value is i+1.
+ InsertHash(temp, jaDtfi.AbbreviatedEnglishEraNames[i - 1], TokenType.JapaneseEraToken, i);
+ }
+ }
+ }
+ // TODO: This prohibits similar custom cultures, but we hard coded the name
+ else if (CultureName.Equals("zh-TW"))
+ {
+ DateTimeFormatInfo twDtfi = GetTaiwanCalendarDTFI();
+ for (int i = 1; i <= twDtfi.Calendar.Eras.Length; i++)
+ {
+ if (twDtfi.GetEraName(i).Length > 0)
+ {
+ InsertHash(temp, twDtfi.GetEraName(i), TokenType.TEraToken, i);
+ }
+ }
+ }
+
+ InsertHash(temp, InvariantInfo.AMDesignator, TokenType.SEP_Am | TokenType.Am, 0);
+ InsertHash(temp, InvariantInfo.PMDesignator, TokenType.SEP_Pm | TokenType.Pm, 1);
+
+ // Add invariant month names and day names.
+ for (int i = 1; i <= 12; i++)
+ {
+ String str;
+ // We have to call public methods here to work with inherited DTFI.
+ // Insert the month name first, so that they are at the front of abbrevaited
+ // month names.
+ str = InvariantInfo.GetMonthName(i);
+ InsertHash(temp, str, TokenType.MonthToken, i);
+ str = InvariantInfo.GetAbbreviatedMonthName(i);
+ InsertHash(temp, str, TokenType.MonthToken, i);
+ }
+
+ for (int i = 0; i < 7; i++)
+ {
+ // We have to call public methods here to work with inherited DTFI.
+ String str = InvariantInfo.GetDayName((DayOfWeek)i);
+ InsertHash(temp, str, TokenType.DayOfWeekToken, i);
+
+ str = InvariantInfo.GetAbbreviatedDayName((DayOfWeek)i);
+ InsertHash(temp, str, TokenType.DayOfWeekToken, i);
+ }
+
+ for (int i = 0; i < AbbreviatedEnglishEraNames.Length; i++)
+ {
+ // m_abbrevEnglishEraNames[0] contains the name for era 1, so the token value is i+1.
+ InsertHash(temp, AbbreviatedEnglishEraNames[i], TokenType.EraToken, i + 1);
+ }
+
+ InsertHash(temp, LocalTimeMark, TokenType.SEP_LocalTimeMark, 0);
+ InsertHash(temp, GMTName, TokenType.TimeZoneToken, 0);
+ InsertHash(temp, ZuluName, TokenType.TimeZoneToken, 0);
+
+ InsertHash(temp, invariantDateSeparator, TokenType.SEP_Date, 0);
+ InsertHash(temp, invariantTimeSeparator, TokenType.SEP_Time, 0);
+
+ _dtfiTokenHash = temp;
+ }
+ return (temp);
+ }
+
+ private void AddMonthNames(TokenHashValue[] temp, String monthPostfix)
+ {
+ for (int i = 1; i <= 13; i++)
+ {
+ String str;
+ //str = internalGetMonthName(i, MonthNameStyles.Regular, false);
+ // We have to call public methods here to work with inherited DTFI.
+ // Insert the month name first, so that they are at the front of abbrevaited
+ // month names.
+ str = GetMonthName(i);
+ if (str.Length > 0)
+ {
+ if (monthPostfix != null)
+ {
+ // Insert the month name with the postfix first, so it can be matched first.
+ InsertHash(temp, str + monthPostfix, TokenType.MonthToken, i);
+ }
+ else
+ {
+ InsertHash(temp, str, TokenType.MonthToken, i);
+ }
+ }
+ str = GetAbbreviatedMonthName(i);
+ InsertHash(temp, str, TokenType.MonthToken, i);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Actions:
+ // Try to parse the current word to see if it is a Hebrew number.
+ // Tokens will be updated accordingly.
+ // This is called by the Lexer of DateTime.Parse().
+ //
+ // Unlike most of the functions in this class, the return value indicates
+ // whether or not it started to parse. The badFormat parameter indicates
+ // if parsing began, but the format was bad.
+ //
+ ////////////////////////////////////////////////////////////////////////
+
+ private static bool TryParseHebrewNumber(
+ ref __DTString str,
+ out Boolean badFormat,
+ out int number)
+ {
+ number = -1;
+ badFormat = false;
+
+ int i = str.Index;
+ if (!HebrewNumber.IsDigit(str.Value[i]))
+ {
+ // If the current character is not a Hebrew digit, just return false.
+ // There is no chance that we can parse a valid Hebrew number from here.
+ return (false);
+ }
+ // The current character is a Hebrew digit. Try to parse this word as a Hebrew number.
+ HebrewNumberParsingContext context = new HebrewNumberParsingContext(0);
+ HebrewNumberParsingState state;
+
+ do
+ {
+ state = HebrewNumber.ParseByChar(str.Value[i++], ref context);
+ switch (state)
+ {
+ case HebrewNumberParsingState.InvalidHebrewNumber: // Not a valid Hebrew number.
+ case HebrewNumberParsingState.NotHebrewDigit: // The current character is not a Hebrew digit character.
+ // Break out so that we don't continue to try parse this as a Hebrew number.
+ return (false);
+ }
+ } while (i < str.Value.Length && (state != HebrewNumberParsingState.FoundEndOfHebrewNumber));
+
+ // When we are here, we are either at the end of the string, or we find a valid Hebrew number.
+ Debug.Assert(state == HebrewNumberParsingState.ContinueParsing || state == HebrewNumberParsingState.FoundEndOfHebrewNumber,
+ "Invalid returned state from HebrewNumber.ParseByChar()");
+
+ if (state != HebrewNumberParsingState.FoundEndOfHebrewNumber)
+ {
+ // We reach end of the string but we can't find a terminal state in parsing Hebrew number.
+ return (false);
+ }
+
+ // We have found a valid Hebrew number. Update the index.
+ str.Advance(i - str.Index);
+
+ // Get the final Hebrew number value from the HebrewNumberParsingContext.
+ number = context.result;
+
+ return (true);
+ }
+
+ private static bool IsHebrewChar(char ch)
+ {
+ return (ch >= '\x0590' && ch <= '\x05ff');
+ }
+
+ internal bool Tokenize(TokenType TokenMask, out TokenType tokenType, out int tokenValue,
+ ref __DTString str)
+ {
+ tokenType = TokenType.UnknownToken;
+ tokenValue = 0;
+
+ TokenHashValue value;
+ Debug.Assert(str.Index < str.Value.Length, "DateTimeFormatInfo.Tokenize(): start < value.Length");
+
+ char ch = str.m_current;
+ bool isLetter = Char.IsLetter(ch);
+ if (isLetter)
+ {
+ ch = this.Culture.TextInfo.ToLower(ch);
+ if (IsHebrewChar(ch) && TokenMask == TokenType.RegularTokenMask)
+ {
+ bool badFormat;
+ if (TryParseHebrewNumber(ref str, out badFormat, out tokenValue))
+ {
+ if (badFormat)
+ {
+ tokenType = TokenType.UnknownToken;
+ return (false);
+ }
+ // This is a Hebrew number.
+ // Do nothing here. TryParseHebrewNumber() will update token accordingly.
+ tokenType = TokenType.HebrewNumber;
+ return (true);
+ }
+ }
+ }
+
+
+ int hashcode = ch % TOKEN_HASH_SIZE;
+ int hashProbe = 1 + ch % SECOND_PRIME;
+ int remaining = str.len - str.Index;
+ int i = 0;
+
+ TokenHashValue[] hashTable = _dtfiTokenHash;
+ if (hashTable == null)
+ {
+ hashTable = CreateTokenHashTable();
+ }
+ do
+ {
+ value = hashTable[hashcode];
+ if (value == null)
+ {
+ // Not found.
+ break;
+ }
+ // Check this value has the right category (regular token or separator token) that we are looking for.
+ if (((int)value.tokenType & (int)TokenMask) > 0 && value.tokenString.Length <= remaining)
+ {
+ bool compareStrings = true;
+ if (isLetter)
+ {
+ // If this token starts with a letter, make sure that we won't allow partial match. So you can't tokenize "MarchWed" separately.
+ // Also an optimization to avoid string comparison
+ int nextCharIndex = str.Index + value.tokenString.Length;
+ if (nextCharIndex > str.len)
+ {
+ compareStrings = false;
+ }
+ else if (nextCharIndex < str.len)
+ {
+ // Check word boundary. The next character should NOT be a letter.
+ char nextCh = str.Value[nextCharIndex];
+ compareStrings = !(Char.IsLetter(nextCh));
+ }
+ }
+ if (compareStrings && CompareStringIgnoreCaseOptimized(str.Value, str.Index, value.tokenString.Length, value.tokenString, 0, value.tokenString.Length))
+ {
+ tokenType = value.tokenType & TokenMask;
+ tokenValue = value.tokenValue;
+ str.Advance(value.tokenString.Length);
+ return (true);
+ }
+ else if ((value.tokenType == TokenType.MonthToken && HasSpacesInMonthNames) ||
+ (value.tokenType == TokenType.DayOfWeekToken && HasSpacesInDayNames))
+ {
+ // For month or day token, we will match the names which have spaces.
+ int matchStrLen = 0;
+ if (str.MatchSpecifiedWords(value.tokenString, true, ref matchStrLen))
+ {
+ tokenType = value.tokenType & TokenMask;
+ tokenValue = value.tokenValue;
+ str.Advance(matchStrLen);
+ return (true);
+ }
+ }
+ }
+ i++;
+ hashcode += hashProbe;
+ if (hashcode >= TOKEN_HASH_SIZE) hashcode -= TOKEN_HASH_SIZE;
+ } while (i < TOKEN_HASH_SIZE);
+
+ return (false);
+ }
+
+ private void InsertAtCurrentHashNode(TokenHashValue[] hashTable, String str, char ch, TokenType tokenType, int tokenValue, int pos, int hashcode, int hashProbe)
+ {
+ // Remember the current slot.
+ TokenHashValue previousNode = hashTable[hashcode];
+
+ //// Console.WriteLine(" Insert Key: {0} in {1}", str, slotToInsert);
+ // Insert the new node into the current slot.
+ hashTable[hashcode] = new TokenHashValue(str, tokenType, tokenValue); ;
+
+ while (++pos < TOKEN_HASH_SIZE)
+ {
+ hashcode += hashProbe;
+ if (hashcode >= TOKEN_HASH_SIZE) hashcode -= TOKEN_HASH_SIZE;
+ // Remember this slot
+ TokenHashValue temp = hashTable[hashcode];
+
+ if (temp != null && this.Culture.TextInfo.ToLower(temp.tokenString[0]) != ch)
+ {
+ continue;
+ }
+ // Put the previous slot into this slot.
+ hashTable[hashcode] = previousNode;
+ //// Console.WriteLine(" Move {0} to slot {1}", previousNode.tokenString, hashcode);
+ if (temp == null)
+ {
+ // Done
+ return;
+ }
+ previousNode = temp;
+ };
+ Debug.Assert(false, "The hashtable is full. This should not happen.");
+ }
+
+ private void InsertHash(TokenHashValue[] hashTable, String str, TokenType tokenType, int tokenValue)
+ {
+ // The month of the 13th month is allowed to be null, so make sure that we ignore null value here.
+ if (str == null || str.Length == 0)
+ {
+ return;
+ }
+ TokenHashValue value;
+ int i = 0;
+ // If there is whitespace characters in the beginning and end of the string, trim them since whitespaces are skipped by
+ // DateTime.Parse().
+ if (Char.IsWhiteSpace(str[0]) || Char.IsWhiteSpace(str[str.Length - 1]))
+ {
+ str = str.Trim(null); // Trim white space characters.
+ // Could have space for separators
+ if (str.Length == 0)
+ return;
+ }
+ char ch = this.Culture.TextInfo.ToLower(str[0]);
+ int hashcode = ch % TOKEN_HASH_SIZE;
+ int hashProbe = 1 + ch % SECOND_PRIME;
+ do
+ {
+ value = hashTable[hashcode];
+ if (value == null)
+ {
+ //// Console.WriteLine(" Put Key: {0} in {1}", str, hashcode);
+ hashTable[hashcode] = new TokenHashValue(str, tokenType, tokenValue);
+ return;
+ }
+ else
+ {
+ // Collision happens. Find another slot.
+ if (str.Length >= value.tokenString.Length)
+ {
+ // If there are two tokens with the same prefix, we have to make sure that the longer token should be at the front of
+ // the shorter ones.
+ if (this.CompareStringIgnoreCaseOptimized(str, 0, value.tokenString.Length, value.tokenString, 0, value.tokenString.Length))
+ {
+ if (str.Length > value.tokenString.Length)
+ {
+ // The str to be inserted has the same prefix as the current token, and str is longer.
+ // Insert str into this node, and shift every node behind it.
+ InsertAtCurrentHashNode(hashTable, str, ch, tokenType, tokenValue, i, hashcode, hashProbe);
+ return;
+ }
+ else
+ {
+ // Same token. If they have different types (regular token vs separator token). Add them.
+ // If we have the same regular token or separator token in the hash already, do NOT update the hash.
+ // Therefore, the order of inserting token is significant here regarding what tokenType will be kept in the hash.
+
+
+ //
+ // Check the current value of RegularToken (stored in the lower 8-bit of tokenType) , and insert the tokenType into the hash ONLY when we don't have a RegularToken yet.
+ // Also check the current value of SeparatorToken (stored in the upper 8-bit of token), and insert the tokenType into the hash ONLY when we don't have the SeparatorToken yet.
+ //
+
+ int nTokenType = (int)tokenType;
+ int nCurrentTokenTypeInHash = (int)value.tokenType;
+
+ //
+ // The folowing is the fix for the issue of throwing FormatException when "mar" is passed in string of the short date format dd/MMM/yyyy for es-MX
+ //
+
+ if (((nCurrentTokenTypeInHash & (int)TokenType.RegularTokenMask) == 0) && ((nTokenType & (int)TokenType.RegularTokenMask) != 0) ||
+ ((nCurrentTokenTypeInHash & (int)TokenType.SeparatorTokenMask) == 0) && ((nTokenType & (int)TokenType.SeparatorTokenMask) != 0))
+ {
+ value.tokenType |= tokenType;
+ if (tokenValue != 0)
+ {
+ value.tokenValue = tokenValue;
+ }
+ }
+ // The token to be inserted is already in the table. Skip it.
+ return;
+ }
+ }
+ }
+ }
+ //// Console.WriteLine(" COLLISION. Old Key: {0}, New Key: {1}", hashTable[hashcode].tokenString, str);
+ i++;
+ hashcode += hashProbe;
+ if (hashcode >= TOKEN_HASH_SIZE) hashcode -= TOKEN_HASH_SIZE;
+ } while (i < TOKEN_HASH_SIZE);
+ Debug.Assert(false, "The hashtable is full. This should not happen.");
+ }
+
+ private bool CompareStringIgnoreCaseOptimized(string string1, int offset1, int length1, string string2, int offset2, int length2)
+ {
+ // Optimize for one character cases which are common due to date and time separators (/ and :)
+ if (length1 == 1 && length2 == 1 && string1[offset1] == string2[offset2])
+ {
+ return true;
+ }
+
+ return (this.Culture.CompareInfo.Compare(string1, offset1, length1, string2, offset2, length2, CompareOptions.IgnoreCase) == 0);
+ }
+
+ // class DateTimeFormatInfo
+
+ internal class TokenHashValue
+ {
+ internal String tokenString;
+ internal TokenType tokenType;
+ internal int tokenValue;
+
+ internal TokenHashValue(String tokenString, TokenType tokenType, int tokenValue)
+ {
+ this.tokenString = tokenString;
+ this.tokenType = tokenType;
+ this.tokenValue = tokenValue;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/DateTimeFormatInfoScanner.cs b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfoScanner.cs
new file mode 100644
index 0000000000..15af1b7d84
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/DateTimeFormatInfoScanner.cs
@@ -0,0 +1,739 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+////////////////////////////////////////////////////////////////////////////
+//
+// DateTimeFormatInfoScanner
+//
+// Scan a specified DateTimeFormatInfo to search for data used in DateTime.Parse()
+//
+// The data includes:
+//
+// DateWords: such as "de" used in es-ES (Spanish) LongDatePattern.
+// Postfix: such as "ta" used in fi-FI after the month name.
+//
+// This class is shared among mscorlib.dll and sysglobl.dll.
+// Use conditional CULTURE_AND_REGIONINFO_BUILDER_ONLY to differentiate between
+// methods for mscorlib.dll and sysglobl.dll.
+//
+////////////////////////////////////////////////////////////////////////////
+
+using System.Collections.Generic;
+using System.Text;
+
+namespace System.Globalization
+{
+
+#if CORECLR
+ using StringStringDictionary = Dictionary<string, string>;
+ using StringList = List<string>;
+#else
+ using StringStringDictionary = LowLevelDictionary<string, string>;
+ using StringList = LowLevelList<string>;
+#endif
+
+ //
+ // from LocaleEx.txt header
+ //
+ //; IFORMATFLAGS
+ //; Parsing/formatting flags.
+ internal enum FORMATFLAGS
+ {
+ None = 0x00000000,
+ UseGenitiveMonth = 0x00000001,
+ UseLeapYearMonth = 0x00000002,
+ UseSpacesInMonthNames = 0x00000004,
+ UseHebrewParsing = 0x00000008,
+ UseSpacesInDayNames = 0x00000010, // Has spaces or non-breaking space in the day names.
+ UseDigitPrefixInTokens = 0x00000020, // Has token starting with numbers.
+ }
+
+ internal enum CalendarId : ushort
+ {
+ UNINITIALIZED_VALUE = 0,
+ GREGORIAN = 1, // Gregorian (localized) calendar
+ GREGORIAN_US = 2, // Gregorian (U.S.) calendar
+ JAPAN = 3, // Japanese Emperor Era calendar
+ /* SSS_WARNINGS_OFF */
+ TAIWAN = 4, // Taiwan Era calendar /* SSS_WARNINGS_ON */
+ KOREA = 5, // Korean Tangun Era calendar
+ HIJRI = 6, // Hijri (Arabic Lunar) calendar
+ THAI = 7, // Thai calendar
+ HEBREW = 8, // Hebrew (Lunar) calendar
+ GREGORIAN_ME_FRENCH = 9, // Gregorian Middle East French calendar
+ GREGORIAN_ARABIC = 10, // Gregorian Arabic calendar
+ GREGORIAN_XLIT_ENGLISH = 11, // Gregorian Transliterated English calendar
+ GREGORIAN_XLIT_FRENCH = 12,
+ // Note that all calendars after this point are MANAGED ONLY for now.
+ JULIAN = 13,
+ JAPANESELUNISOLAR = 14,
+ CHINESELUNISOLAR = 15,
+ SAKA = 16, // reserved to match Office but not implemented in our code
+ LUNAR_ETO_CHN = 17, // reserved to match Office but not implemented in our code
+ LUNAR_ETO_KOR = 18, // reserved to match Office but not implemented in our code
+ LUNAR_ETO_ROKUYOU = 19, // reserved to match Office but not implemented in our code
+ KOREANLUNISOLAR = 20,
+ TAIWANLUNISOLAR = 21,
+ PERSIAN = 22,
+ UMALQURA = 23,
+ LAST_CALENDAR = 23 // Last calendar ID
+ }
+
+ internal class DateTimeFormatInfoScanner
+ {
+ // Special prefix-like flag char in DateWord array.
+
+ // Use char in PUA area since we won't be using them in real data.
+ // The char used to tell a read date word or a month postfix. A month postfix
+ // is "ta" in the long date pattern like "d. MMMM'ta 'yyyy" for fi-FI.
+ // In this case, it will be stored as "\xfffeta" in the date word array.
+ internal const char MonthPostfixChar = '\xe000';
+
+ // Add ignorable symbol in a DateWord array.
+
+ // hu-HU has:
+ // shrot date pattern: yyyy. MM. dd.;yyyy-MM-dd;yy-MM-dd
+ // long date pattern: yyyy. MMMM d.
+ // Here, "." is the date separator (derived from short date pattern). However,
+ // "." also appear at the end of long date pattern. In this case, we just
+ // "." as ignorable symbol so that the DateTime.Parse() state machine will not
+ // treat the additional date separator at the end of y,m,d pattern as an error
+ // condition.
+ internal const char IgnorableSymbolChar = '\xe001';
+
+ // Known CJK suffix
+ internal const String CJKYearSuff = "\u5e74";
+ internal const String CJKMonthSuff = "\u6708";
+ internal const String CJKDaySuff = "\u65e5";
+
+ internal const String KoreanYearSuff = "\ub144";
+ internal const String KoreanMonthSuff = "\uc6d4";
+ internal const String KoreanDaySuff = "\uc77c";
+
+ internal const String KoreanHourSuff = "\uc2dc";
+ internal const String KoreanMinuteSuff = "\ubd84";
+ internal const String KoreanSecondSuff = "\ucd08";
+
+ internal const String CJKHourSuff = "\u6642";
+ internal const String ChineseHourSuff = "\u65f6";
+
+ internal const String CJKMinuteSuff = "\u5206";
+ internal const String CJKSecondSuff = "\u79d2";
+
+ // The collection fo date words & postfix.
+ internal StringList m_dateWords = new StringList();
+ // Hashtable for the known words.
+ private static volatile StringStringDictionary s_knownWords;
+
+ static StringStringDictionary KnownWords
+ {
+ get
+ {
+ if (s_knownWords == null)
+ {
+ StringStringDictionary temp = new StringStringDictionary();
+ // Add known words into the hash table.
+
+ // Skip these special symbols.
+ temp.Add("/", String.Empty);
+ temp.Add("-", String.Empty);
+ temp.Add(".", String.Empty);
+ // Skip known CJK suffixes.
+ temp.Add(CJKYearSuff, String.Empty);
+ temp.Add(CJKMonthSuff, String.Empty);
+ temp.Add(CJKDaySuff, String.Empty);
+ temp.Add(KoreanYearSuff, String.Empty);
+ temp.Add(KoreanMonthSuff, String.Empty);
+ temp.Add(KoreanDaySuff, String.Empty);
+ temp.Add(KoreanHourSuff, String.Empty);
+ temp.Add(KoreanMinuteSuff, String.Empty);
+ temp.Add(KoreanSecondSuff, String.Empty);
+ temp.Add(CJKHourSuff, String.Empty);
+ temp.Add(ChineseHourSuff, String.Empty);
+ temp.Add(CJKMinuteSuff, String.Empty);
+ temp.Add(CJKSecondSuff, String.Empty);
+
+ s_knownWords = temp;
+ }
+ return (s_knownWords);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Parameters:
+ // pattern: The pattern to be scanned.
+ // currentIndex: the current index to start the scan.
+ //
+ // Returns:
+ // Return the index with the first character that is a letter, which will
+ // be the start of a date word.
+ // Note that the index can be pattern.Length if we reach the end of the string.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal static int SkipWhiteSpacesAndNonLetter(String pattern, int currentIndex)
+ {
+ while (currentIndex < pattern.Length)
+ {
+ char ch = pattern[currentIndex];
+ if (ch == '\\')
+ {
+ // Escaped character. Look ahead one character.
+ currentIndex++;
+ if (currentIndex < pattern.Length)
+ {
+ ch = pattern[currentIndex];
+ if (ch == '\'')
+ {
+ // Skip the leading single quote. We will
+ // stop at the first letter.
+ continue;
+ }
+ // Fall thru to check if this is a letter.
+ }
+ else
+ {
+ // End of string
+ break;
+ }
+ }
+ if (Char.IsLetter(ch) || ch == '\'' || ch == '.')
+ {
+ break;
+ }
+ // Skip the current char since it is not a letter.
+ currentIndex++;
+ }
+ return (currentIndex);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // A helper to add the found date word or month postfix into ArrayList for date words.
+ //
+ // Parameters:
+ // formatPostfix: What kind of postfix this is.
+ // Possible values:
+ // null: This is a regular date word
+ // "MMMM": month postfix
+ // word: The date word or postfix to be added.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal void AddDateWordOrPostfix(String formatPostfix, String str)
+ {
+ if (str.Length > 0)
+ {
+ // Some cultures use . like an abbreviation
+ if (str.Equals("."))
+ {
+ AddIgnorableSymbols(".");
+ return;
+ }
+ String words;
+ if (KnownWords.TryGetValue(str, out words) == false)
+ {
+ if (m_dateWords == null)
+ {
+ m_dateWords = new StringList();
+ }
+ if (formatPostfix == "MMMM")
+ {
+ // Add the word into the ArrayList as "\xfffe" + real month postfix.
+ String temp = MonthPostfixChar + str;
+ if (!m_dateWords.Contains(temp))
+ {
+ m_dateWords.Add(temp);
+ }
+ }
+ else
+ {
+ if (!m_dateWords.Contains(str))
+ {
+ m_dateWords.Add(str);
+ }
+ if (str[str.Length - 1] == '.')
+ {
+ // Old version ignore the trialing dot in the date words. Support this as well.
+ String strWithoutDot = str.Substring(0, str.Length - 1);
+ if (!m_dateWords.Contains(strWithoutDot))
+ {
+ m_dateWords.Add(strWithoutDot);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Scan the pattern from the specified index and add the date word/postfix
+ // when appropriate.
+ //
+ // Parameters:
+ // pattern: The pattern to be scanned.
+ // index: The starting index to be scanned.
+ // formatPostfix: The kind of postfix to be scanned.
+ // Possible values:
+ // null: This is a regular date word
+ // "MMMM": month postfix
+ //
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal int AddDateWords(String pattern, int index, String formatPostfix)
+ {
+ // Skip any whitespaces so we will start from a letter.
+ int newIndex = SkipWhiteSpacesAndNonLetter(pattern, index);
+ if (newIndex != index && formatPostfix != null)
+ {
+ // There are whitespaces. This will not be a postfix.
+ formatPostfix = null;
+ }
+ index = newIndex;
+
+ // This is the first char added into dateWord.
+ // Skip all non-letter character. We will add the first letter into DateWord.
+ StringBuilder dateWord = new StringBuilder();
+ // We assume that date words should start with a letter.
+ // Skip anything until we see a letter.
+
+ while (index < pattern.Length)
+ {
+ char ch = pattern[index];
+ if (ch == '\'')
+ {
+ // We have seen the end of quote. Add the word if we do not see it before,
+ // and break the while loop.
+ AddDateWordOrPostfix(formatPostfix, dateWord.ToString());
+ index++;
+ break;
+ }
+ else if (ch == '\\')
+ {
+ //
+ // Escaped character. Look ahead one character
+ //
+
+ // Skip escaped backslash.
+ index++;
+ if (index < pattern.Length)
+ {
+ dateWord.Append(pattern[index]);
+ index++;
+ }
+ }
+ else if (Char.IsWhiteSpace(ch))
+ {
+ // Found a whitespace. We have to add the current date word/postfix.
+ AddDateWordOrPostfix(formatPostfix, dateWord.ToString());
+ if (formatPostfix != null)
+ {
+ // Done with postfix. The rest will be regular date word.
+ formatPostfix = null;
+ }
+ // Reset the dateWord.
+ dateWord.Length = 0;
+ index++;
+ }
+ else
+ {
+ dateWord.Append(ch);
+ index++;
+ }
+ }
+ return (index);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // A simple helper to find the repeat count for a specified char.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal static int ScanRepeatChar(String pattern, char ch, int index, out int count)
+ {
+ count = 1;
+ while (++index < pattern.Length && pattern[index] == ch)
+ {
+ count++;
+ }
+ // Return the updated position.
+ return (index);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Add the text that is a date separator but is treated like ignroable symbol.
+ // E.g.
+ // hu-HU has:
+ // shrot date pattern: yyyy. MM. dd.;yyyy-MM-dd;yy-MM-dd
+ // long date pattern: yyyy. MMMM d.
+ // Here, "." is the date separator (derived from short date pattern). However,
+ // "." also appear at the end of long date pattern. In this case, we just
+ // "." as ignorable symbol so that the DateTime.Parse() state machine will not
+ // treat the additional date separator at the end of y,m,d pattern as an error
+ // condition.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ internal void AddIgnorableSymbols(String text)
+ {
+ if (m_dateWords == null)
+ {
+ // Create the date word array.
+ m_dateWords = new StringList();
+ }
+ // Add the ignorable symbol into the ArrayList.
+ String temp = IgnorableSymbolChar + text;
+ if (!m_dateWords.Contains(temp))
+ {
+ m_dateWords.Add(temp);
+ }
+ }
+
+
+ //
+ // Flag used to trace the date patterns (yy/yyyyy/M/MM/MMM/MMM/d/dd) that we have seen.
+ //
+ private enum FoundDatePattern
+ {
+ None = 0x0000,
+ FoundYearPatternFlag = 0x0001,
+ FoundMonthPatternFlag = 0x0002,
+ FoundDayPatternFlag = 0x0004,
+ FoundYMDPatternFlag = 0x0007, // FoundYearPatternFlag | FoundMonthPatternFlag | FoundDayPatternFlag;
+ }
+
+ // Check if we have found all of the year/month/day pattern.
+ private FoundDatePattern _ymdFlags = FoundDatePattern.None;
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Given a date format pattern, scan for date word or postfix.
+ //
+ // A date word should be always put in a single quoted string. And it will
+ // start from a letter, so whitespace and symbols will be ignored before
+ // the first letter.
+ //
+ // Examples of date word:
+ // 'de' in es-SP: dddd, dd' de 'MMMM' de 'yyyy
+ // "\x0443." in bg-BG: dd.M.yyyy '\x0433.'
+ //
+ // Example of postfix:
+ // month postfix:
+ // "ta" in fi-FI: d. MMMM'ta 'yyyy
+ // Currently, only month postfix is supported.
+ //
+ // Usage:
+ // Always call this with Framework-style pattern, instead of Windows style pattern.
+ // Windows style pattern uses '' for single quote, while .NET uses \'
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal void ScanDateWord(String pattern)
+ {
+ // Check if we have found all of the year/month/day pattern.
+ _ymdFlags = FoundDatePattern.None;
+
+ int i = 0;
+ while (i < pattern.Length)
+ {
+ char ch = pattern[i];
+ int chCount;
+
+ switch (ch)
+ {
+ case '\'':
+ // Find a beginning quote. Search until the end quote.
+ i = AddDateWords(pattern, i + 1, null);
+ break;
+ case 'M':
+ i = ScanRepeatChar(pattern, 'M', i, out chCount);
+ if (chCount >= 4)
+ {
+ if (i < pattern.Length && pattern[i] == '\'')
+ {
+ i = AddDateWords(pattern, i + 1, "MMMM");
+ }
+ }
+ _ymdFlags |= FoundDatePattern.FoundMonthPatternFlag;
+ break;
+ case 'y':
+ i = ScanRepeatChar(pattern, 'y', i, out chCount);
+ _ymdFlags |= FoundDatePattern.FoundYearPatternFlag;
+ break;
+ case 'd':
+ i = ScanRepeatChar(pattern, 'd', i, out chCount);
+ if (chCount <= 2)
+ {
+ // Only count "d" & "dd".
+ // ddd, dddd are day names. Do not count them.
+ _ymdFlags |= FoundDatePattern.FoundDayPatternFlag;
+ }
+ break;
+ case '\\':
+ // Found a escaped char not in a quoted string. Skip the current backslash
+ // and its next character.
+ i += 2;
+ break;
+ case '.':
+ if (_ymdFlags == FoundDatePattern.FoundYMDPatternFlag)
+ {
+ // If we find a dot immediately after the we have seen all of the y, m, d pattern.
+ // treat it as a ignroable symbol. Check for comments in AddIgnorableSymbols for
+ // more details.
+ AddIgnorableSymbols(".");
+ _ymdFlags = FoundDatePattern.None;
+ }
+ i++;
+ break;
+ default:
+ if (_ymdFlags == FoundDatePattern.FoundYMDPatternFlag && !Char.IsWhiteSpace(ch))
+ {
+ // We are not seeing "." after YMD. Clear the flag.
+ _ymdFlags = FoundDatePattern.None;
+ }
+ // We are not in quote. Skip the current character.
+ i++;
+ break;
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Given a DTFI, get all of the date words from date patterns and time patterns.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ internal String[] GetDateWordsOfDTFI(DateTimeFormatInfo dtfi)
+ {
+ // Enumarate all LongDatePatterns, and get the DateWords and scan for month postfix.
+ String[] datePatterns = dtfi.GetAllDateTimePatterns('D');
+ int i;
+
+ // Scan the long date patterns
+ for (i = 0; i < datePatterns.Length; i++)
+ {
+ ScanDateWord(datePatterns[i]);
+ }
+
+ // Scan the short date patterns
+ datePatterns = dtfi.GetAllDateTimePatterns('d');
+ for (i = 0; i < datePatterns.Length; i++)
+ {
+ ScanDateWord(datePatterns[i]);
+ }
+ // Scan the YearMonth patterns.
+ datePatterns = dtfi.GetAllDateTimePatterns('y');
+ for (i = 0; i < datePatterns.Length; i++)
+ {
+ ScanDateWord(datePatterns[i]);
+ }
+
+ // Scan the month/day pattern
+ ScanDateWord(dtfi.MonthDayPattern);
+
+ // Scan the long time patterns.
+ datePatterns = dtfi.GetAllDateTimePatterns('T');
+ for (i = 0; i < datePatterns.Length; i++)
+ {
+ ScanDateWord(datePatterns[i]);
+ }
+
+ // Scan the short time patterns.
+ datePatterns = dtfi.GetAllDateTimePatterns('t');
+ for (i = 0; i < datePatterns.Length; i++)
+ {
+ ScanDateWord(datePatterns[i]);
+ }
+
+ String[] result = null;
+ if (m_dateWords != null && m_dateWords.Count > 0)
+ {
+ result = new String[m_dateWords.Count];
+ for (i = 0; i < m_dateWords.Count; i++)
+ {
+ result[i] = m_dateWords[i];
+ }
+ }
+ return (result);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Scan the month names to see if genitive month names are used, and return
+ // the format flag.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal static FORMATFLAGS GetFormatFlagGenitiveMonth(String[] monthNames, String[] genitveMonthNames, String[] abbrevMonthNames, String[] genetiveAbbrevMonthNames)
+ {
+ // If we have different names in regular and genitive month names, use genitive month flag.
+ return ((!EqualStringArrays(monthNames, genitveMonthNames) || !EqualStringArrays(abbrevMonthNames, genetiveAbbrevMonthNames))
+ ? FORMATFLAGS.UseGenitiveMonth : 0);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Scan the month names to see if spaces are used or start with a digit, and return the format flag
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal static FORMATFLAGS GetFormatFlagUseSpaceInMonthNames(String[] monthNames, String[] genitveMonthNames, String[] abbrevMonthNames, String[] genetiveAbbrevMonthNames)
+ {
+ FORMATFLAGS formatFlags = 0;
+ formatFlags |= (ArrayElementsBeginWithDigit(monthNames) ||
+ ArrayElementsBeginWithDigit(genitveMonthNames) ||
+ ArrayElementsBeginWithDigit(abbrevMonthNames) ||
+ ArrayElementsBeginWithDigit(genetiveAbbrevMonthNames)
+ ? FORMATFLAGS.UseDigitPrefixInTokens : 0);
+
+ formatFlags |= (ArrayElementsHaveSpace(monthNames) ||
+ ArrayElementsHaveSpace(genitveMonthNames) ||
+ ArrayElementsHaveSpace(abbrevMonthNames) ||
+ ArrayElementsHaveSpace(genetiveAbbrevMonthNames)
+ ? FORMATFLAGS.UseSpacesInMonthNames : 0);
+ return (formatFlags);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Scan the day names and set the correct format flag.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal static FORMATFLAGS GetFormatFlagUseSpaceInDayNames(String[] dayNames, String[] abbrevDayNames)
+ {
+ return ((ArrayElementsHaveSpace(dayNames) ||
+ ArrayElementsHaveSpace(abbrevDayNames))
+ ? FORMATFLAGS.UseSpacesInDayNames : 0);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Check the calendar to see if it is HebrewCalendar and set the Hebrew format flag if necessary.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ internal static FORMATFLAGS GetFormatFlagUseHebrewCalendar(int calID)
+ {
+ return (calID == (int)CalendarId.HEBREW ?
+ FORMATFLAGS.UseHebrewParsing | FORMATFLAGS.UseLeapYearMonth : 0);
+ }
+
+
+ //-----------------------------------------------------------------------------
+ // EqualStringArrays
+ // compares two string arrays and return true if all elements of the first
+ // array equals to all elmentsof the second array.
+ // otherwise it returns false.
+ //-----------------------------------------------------------------------------
+
+ private static bool EqualStringArrays(string[] array1, string[] array2)
+ {
+ // Shortcut if they're the same array
+ if (array1 == array2)
+ {
+ return true;
+ }
+
+ // This is effectively impossible
+ if (array1.Length != array2.Length)
+ {
+ return false;
+ }
+
+ // Check each string
+ for (int i = 0; i < array1.Length; i++)
+ {
+ if (!array1[i].Equals(array2[i]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //-----------------------------------------------------------------------------
+ // ArrayElementsHaveSpace
+ // It checks all input array elements if any of them has space character
+ // returns true if found space character in one of the array elements.
+ // otherwise returns false.
+ //-----------------------------------------------------------------------------
+
+ private static bool ArrayElementsHaveSpace(string[] array)
+ {
+ for (int i = 0; i < array.Length; i++)
+ {
+ // it is faster to check for space character manually instead of calling IndexOf
+ // so we don't have to go to native code side.
+ for (int j = 0; j < array[i].Length; j++)
+ {
+ if (Char.IsWhiteSpace(array[i][j]))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Check if any element of the array start with a digit.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static bool ArrayElementsBeginWithDigit(string[] array)
+ {
+ for (int i = 0; i < array.Length; i++)
+ {
+ // it is faster to check for space character manually instead of calling IndexOf
+ // so we don't have to go to native code side.
+ if (array[i].Length > 0 &&
+ array[i][0] >= '0' && array[i][0] <= '9')
+ {
+ int index = 1;
+ while (index < array[i].Length && array[i][index] >= '0' && array[i][index] <= '9')
+ {
+ // Skip other digits.
+ index++;
+ }
+ if (index == array[i].Length)
+ {
+ return (false);
+ }
+
+ if (index == array[i].Length - 1)
+ {
+ // Skip known CJK month suffix.
+ // CJK uses month name like "1\x6708", since \x6708 is a known month suffix,
+ // we don't need the UseDigitPrefixInTokens since it is slower.
+ switch (array[i][index])
+ {
+ case '\x6708': // CJKMonthSuff
+ case '\xc6d4': // KoreanMonthSuff
+ return (false);
+ }
+ }
+
+ if (index == array[i].Length - 4)
+ {
+ // Skip known CJK month suffix.
+ // Starting with Windows 8, the CJK months for some cultures looks like: "1' \x6708'"
+ // instead of just "1\x6708"
+ if (array[i][index] == '\'' && array[i][index + 1] == ' ' &&
+ array[i][index + 2] == '\x6708' && array[i][index + 3] == '\'')
+ {
+ return (false);
+ }
+ }
+ return (true);
+ }
+ }
+
+ return false;
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Globalization/DateTimeParse.cs b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs
new file mode 100644
index 0000000000..910fbf2ff0
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/DateTimeParse.cs
@@ -0,0 +1,5672 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+using System.Text;
+
+namespace System
+{
+ internal static class DateTimeParse
+ {
+ internal const Int32 MaxDateTimeNumberDigits = 8;
+
+ internal delegate bool MatchNumberDelegate(ref __DTString str, int digitLen, out int result);
+
+ internal static MatchNumberDelegate m_hebrewNumberParser = new MatchNumberDelegate(DateTimeParse.MatchHebrewDigits);
+
+ internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style)
+ {
+ DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
+ result.Init();
+ if (TryParseExact(s, format, dtfi, style, ref result))
+ {
+ return result.parsedDate;
+ }
+ else
+ {
+ throw GetDateTimeParseException(ref result);
+ }
+ }
+
+ internal static DateTime ParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset)
+ {
+ DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
+ offset = TimeSpan.Zero;
+ result.Init();
+ result.flags |= ParseFlags.CaptureOffset;
+ if (TryParseExact(s, format, dtfi, style, ref result))
+ {
+ offset = result.timeZoneOffset;
+ return result.parsedDate;
+ }
+ else
+ {
+ throw GetDateTimeParseException(ref result);
+ }
+ }
+
+ internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result)
+ {
+ result = DateTime.MinValue;
+ DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result.
+ resultData.Init();
+ if (TryParseExact(s, format, dtfi, style, ref resultData))
+ {
+ result = resultData.parsedDate;
+ return true;
+ }
+ return false;
+ }
+
+ internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset)
+ {
+ result = DateTime.MinValue;
+ offset = TimeSpan.Zero;
+ DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result.
+ resultData.Init();
+ resultData.flags |= ParseFlags.CaptureOffset;
+ if (TryParseExact(s, format, dtfi, style, ref resultData))
+ {
+ result = resultData.parsedDate;
+ offset = resultData.timeZoneOffset;
+ return true;
+ }
+ return false;
+ }
+
+ internal static bool TryParseExact(String s, String format, DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result)
+ {
+ if (s == null)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s));
+ return false;
+ }
+ if (format == null)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(format));
+ return false;
+ }
+ if (s.Length == 0)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ if (format.Length == 0)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ return false;
+ }
+
+ Debug.Assert(dtfi != null, "dtfi == null");
+
+ return DoStrictParse(s, format, style, dtfi, ref result);
+ }
+
+ internal static DateTime ParseExactMultiple(String s, String[] formats,
+ DateTimeFormatInfo dtfi, DateTimeStyles style)
+ {
+ DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
+ result.Init();
+ if (TryParseExactMultiple(s, formats, dtfi, style, ref result))
+ {
+ return result.parsedDate;
+ }
+ else
+ {
+ throw GetDateTimeParseException(ref result);
+ }
+ }
+
+
+ internal static DateTime ParseExactMultiple(String s, String[] formats,
+ DateTimeFormatInfo dtfi, DateTimeStyles style, out TimeSpan offset)
+ {
+ DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
+ offset = TimeSpan.Zero;
+ result.Init();
+ result.flags |= ParseFlags.CaptureOffset;
+ if (TryParseExactMultiple(s, formats, dtfi, style, ref result))
+ {
+ offset = result.timeZoneOffset;
+ return result.parsedDate;
+ }
+ else
+ {
+ throw GetDateTimeParseException(ref result);
+ }
+ }
+
+ internal static bool TryParseExactMultiple(String s, String[] formats,
+ DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result, out TimeSpan offset)
+ {
+ result = DateTime.MinValue;
+ offset = TimeSpan.Zero;
+ DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result.
+ resultData.Init();
+ resultData.flags |= ParseFlags.CaptureOffset;
+ if (TryParseExactMultiple(s, formats, dtfi, style, ref resultData))
+ {
+ result = resultData.parsedDate;
+ offset = resultData.timeZoneOffset;
+ return true;
+ }
+ return false;
+ }
+
+
+ internal static bool TryParseExactMultiple(String s, String[] formats,
+ DateTimeFormatInfo dtfi, DateTimeStyles style, out DateTime result)
+ {
+ result = DateTime.MinValue;
+ DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result.
+ resultData.Init();
+ if (TryParseExactMultiple(s, formats, dtfi, style, ref resultData))
+ {
+ result = resultData.parsedDate;
+ return true;
+ }
+ return false;
+ }
+
+ internal static bool TryParseExactMultiple(String s, String[] formats,
+ DateTimeFormatInfo dtfi, DateTimeStyles style, ref DateTimeResult result)
+ {
+ if (s == null)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s));
+ return false;
+ }
+ if (formats == null)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(formats));
+ return false;
+ }
+
+ if (s.Length == 0)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ if (formats.Length == 0)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ return false;
+ }
+
+ Debug.Assert(dtfi != null, "dtfi == null");
+
+ //
+ // Do a loop through the provided formats and see if we can parse succesfully in
+ // one of the formats.
+ //
+ for (int i = 0; i < formats.Length; i++)
+ {
+ if (formats[i] == null || formats[i].Length == 0)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ return false;
+ }
+ // Create a new result each time to ensure the runs are independent. Carry through
+ // flags from the caller and return the result.
+ DateTimeResult innerResult = new DateTimeResult(); // The buffer to store the parsing result.
+ innerResult.Init();
+ innerResult.flags = result.flags;
+ if (TryParseExact(s, formats[i], dtfi, style, ref innerResult))
+ {
+ result.parsedDate = innerResult.parsedDate;
+ result.timeZoneOffset = innerResult.timeZoneOffset;
+ return (true);
+ }
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Date Token Types
+ //
+ // Following is the set of tokens that can be generated from a date
+ // string. Notice that the legal set of trailing separators have been
+ // folded in with the date number, and month name tokens. This set
+ // of tokens is chosen to reduce the number of date parse states.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ internal enum DTT : int
+ {
+ End = 0, // '\0'
+ NumEnd = 1, // Num[ ]*[\0]
+ NumAmpm = 2, // Num[ ]+AmPm
+ NumSpace = 3, // Num[ ]+^[Dsep|Tsep|'0\']
+ NumDatesep = 4, // Num[ ]*Dsep
+ NumTimesep = 5, // Num[ ]*Tsep
+ MonthEnd = 6, // Month[ ]*'\0'
+ MonthSpace = 7, // Month[ ]+^[Dsep|Tsep|'\0']
+ MonthDatesep = 8, // Month[ ]*Dsep
+ NumDatesuff = 9, // Month[ ]*DSuff
+ NumTimesuff = 10, // Month[ ]*TSuff
+ DayOfWeek = 11, // Day of week name
+ YearSpace = 12, // Year+^[Dsep|Tsep|'0\']
+ YearDateSep = 13, // Year+Dsep
+ YearEnd = 14, // Year+['\0']
+ TimeZone = 15, // timezone name
+ Era = 16, // era name
+ NumUTCTimeMark = 17, // Num + 'Z'
+ // When you add a new token which will be in the
+ // state table, add it after NumLocalTimeMark.
+ Unk = 18, // unknown
+ NumLocalTimeMark = 19, // Num + 'T'
+ Max = 20, // marker
+ }
+
+ internal enum TM
+ {
+ NotSet = -1,
+ AM = 0,
+ PM = 1,
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // DateTime parsing state enumeration (DS.*)
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ internal enum DS
+ {
+ BEGIN = 0,
+ N = 1, // have one number
+ NN = 2, // have two numbers
+
+ // The following are known to be part of a date
+
+ D_Nd = 3, // date string: have number followed by date separator
+ D_NN = 4, // date string: have two numbers
+ D_NNd = 5, // date string: have two numbers followed by date separator
+
+ D_M = 6, // date string: have a month
+ D_MN = 7, // date string: have a month and a number
+ D_NM = 8, // date string: have a number and a month
+ D_MNd = 9, // date string: have a month and number followed by date separator
+ D_NDS = 10, // date string: have one number followed a date suffix.
+
+ D_Y = 11, // date string: have a year.
+ D_YN = 12, // date string: have a year and a number
+ D_YNd = 13, // date string: have a year and a number and a date separator
+ D_YM = 14, // date string: have a year and a month
+ D_YMd = 15, // date string: have a year and a month and a date separator
+ D_S = 16, // have numbers followed by a date suffix.
+ T_S = 17, // have numbers followed by a time suffix.
+
+ // The following are known to be part of a time
+
+ T_Nt = 18, // have num followed by time separator
+ T_NNt = 19, // have two numbers followed by time separator
+
+
+ ERROR = 20,
+
+ // The following are terminal states. These all have an action
+ // associated with them; and transition back to BEGIN.
+
+ DX_NN = 21, // day from two numbers
+ DX_NNN = 22, // day from three numbers
+ DX_MN = 23, // day from month and one number
+ DX_NM = 24, // day from month and one number
+ DX_MNN = 25, // day from month and two numbers
+ DX_DS = 26, // a set of date suffixed numbers.
+ DX_DSN = 27, // day from date suffixes and one number.
+ DX_NDS = 28, // day from one number and date suffixes .
+ DX_NNDS = 29, // day from one number and date suffixes .
+
+ DX_YNN = 30, // date string: have a year and two number
+ DX_YMN = 31, // date string: have a year, a month, and a number.
+ DX_YN = 32, // date string: have a year and one number
+ DX_YM = 33, // date string: have a year, a month.
+ TX_N = 34, // time from one number (must have ampm)
+ TX_NN = 35, // time from two numbers
+ TX_NNN = 36, // time from three numbers
+ TX_TS = 37, // a set of time suffixed numbers.
+ DX_NNY = 38,
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // NOTE: The following state machine table is dependent on the order of the
+ // DS and DTT enumerations.
+ //
+ // For each non terminal state, the following table defines the next state
+ // for each given date token type.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ // End NumEnd NumAmPm NumSpace NumDaySep NumTimesep MonthEnd MonthSpace MonthDSep NumDateSuff NumTimeSuff DayOfWeek YearSpace YearDateSep YearEnd TimeZone Era UTCTimeMark
+ private static DS[][] dateParsingStates = {
+// DS.BEGIN // DS.BEGIN
+new DS[] { DS.BEGIN, DS.ERROR, DS.TX_N, DS.N, DS.D_Nd, DS.T_Nt, DS.ERROR, DS.D_M, DS.D_M, DS.D_S, DS.T_S, DS.BEGIN, DS.D_Y, DS.D_Y, DS.ERROR, DS.BEGIN, DS.BEGIN, DS.ERROR},
+
+// DS.N // DS.N
+new DS[] { DS.ERROR, DS.DX_NN, DS.ERROR, DS.NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_NM, DS.D_MNd, DS.D_NDS, DS.ERROR, DS.N, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.N, DS.N, DS.ERROR},
+
+// DS.NN // DS.NN
+new DS[] { DS.DX_NN, DS.DX_NNN, DS.TX_N, DS.DX_NNN, DS.ERROR, DS.T_Nt, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.ERROR, DS.T_S, DS.NN, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.NN, DS.NN, DS.ERROR},
+
+// DS.D_Nd // DS.D_Nd
+new DS[] { DS.ERROR, DS.DX_NN, DS.ERROR, DS.D_NN, DS.D_NNd, DS.ERROR, DS.DX_NM, DS.D_MN, DS.D_MNd, DS.ERROR, DS.ERROR, DS.D_Nd, DS.D_YN, DS.D_YNd, DS.DX_YN, DS.ERROR, DS.D_Nd, DS.ERROR},
+
+// DS.D_NN // DS.D_NN
+new DS[] { DS.DX_NN, DS.DX_NNN, DS.TX_N, DS.DX_NNN, DS.ERROR, DS.T_Nt, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.DX_DS, DS.T_S, DS.D_NN, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.ERROR, DS.D_NN, DS.ERROR},
+
+// DS.D_NNd // DS.D_NNd
+new DS[] { DS.ERROR, DS.DX_NNN, DS.DX_NNN, DS.DX_NNN, DS.ERROR, DS.ERROR, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.DX_DS, DS.ERROR, DS.D_NNd, DS.DX_NNY, DS.ERROR, DS.DX_NNY, DS.ERROR, DS.D_NNd, DS.ERROR},
+
+// DS.D_M // DS.D_M
+new DS[] { DS.ERROR, DS.DX_MN, DS.ERROR, DS.D_MN, DS.D_MNd, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_M, DS.D_YM, DS.D_YMd, DS.DX_YM, DS.ERROR, DS.D_M, DS.ERROR},
+
+// DS.D_MN // DS.D_MN
+new DS[] { DS.DX_MN, DS.DX_MNN, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.T_Nt, DS.ERROR, DS.ERROR, DS.ERROR, DS.DX_DS, DS.T_S, DS.D_MN, DS.DX_YMN, DS.ERROR, DS.DX_YMN, DS.ERROR, DS.D_MN, DS.ERROR},
+
+// DS.D_NM // DS.D_NM
+new DS[] { DS.DX_NM, DS.DX_MNN, DS.DX_MNN, DS.DX_MNN, DS.ERROR, DS.T_Nt, DS.ERROR, DS.ERROR, DS.ERROR, DS.DX_DS, DS.T_S, DS.D_NM, DS.DX_YMN, DS.ERROR, DS.DX_YMN, DS.ERROR, DS.D_NM, DS.ERROR},
+
+// DS.D_MNd // DS.D_MNd
+new DS[] { DS.ERROR, DS.DX_MNN, DS.ERROR, DS.DX_MNN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_MNd, DS.DX_YMN, DS.ERROR, DS.DX_YMN, DS.ERROR, DS.D_MNd, DS.ERROR},
+
+// DS.D_NDS, // DS.D_NDS,
+new DS[] { DS.DX_NDS,DS.DX_NNDS, DS.DX_NNDS, DS.DX_NNDS, DS.ERROR, DS.T_Nt, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_NDS, DS.T_S, DS.D_NDS, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_NDS, DS.ERROR},
+
+// DS.D_Y // DS.D_Y
+new DS[] { DS.ERROR, DS.DX_YN, DS.ERROR, DS.D_YN, DS.D_YNd, DS.ERROR, DS.DX_YM, DS.D_YM, DS.D_YMd, DS.D_YM, DS.ERROR, DS.D_Y, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_Y, DS.ERROR},
+
+// DS.D_YN // DS.D_YN
+new DS[] { DS.DX_YN, DS.DX_YNN, DS.DX_YNN, DS.DX_YNN, DS.ERROR, DS.ERROR, DS.DX_YMN, DS.DX_YMN, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YN, DS.ERROR},
+
+// DS.D_YNd // DS.D_YNd
+new DS[] { DS.ERROR, DS.DX_YNN, DS.DX_YNN, DS.DX_YNN, DS.ERROR, DS.ERROR, DS.DX_YMN, DS.DX_YMN, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YN, DS.ERROR},
+
+// DS.D_YM // DS.D_YM
+new DS[] { DS.DX_YM, DS.DX_YMN, DS.DX_YMN, DS.DX_YMN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YM, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YM, DS.ERROR},
+
+// DS.D_YMd // DS.D_YMd
+new DS[] { DS.ERROR, DS.DX_YMN, DS.DX_YMN, DS.DX_YMN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YM, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_YM, DS.ERROR},
+
+// DS.D_S // DS.D_S
+new DS[] { DS.DX_DS, DS.DX_DSN, DS.TX_N, DS.T_Nt, DS.ERROR, DS.T_Nt, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_S, DS.T_S, DS.D_S, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_S, DS.ERROR},
+
+// DS.T_S // DS.T_S
+new DS[] { DS.TX_TS, DS.TX_TS, DS.TX_TS, DS.T_Nt, DS.D_Nd, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.D_S, DS.T_S, DS.T_S, DS.ERROR, DS.ERROR, DS.ERROR, DS.T_S, DS.T_S, DS.ERROR},
+
+// DS.T_Nt // DS.T_Nt
+new DS[] { DS.ERROR, DS.TX_NN, DS.TX_NN, DS.TX_NN, DS.ERROR, DS.T_NNt, DS.DX_NM, DS.D_NM, DS.ERROR, DS.ERROR, DS.T_S, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.T_Nt, DS.T_Nt, DS.TX_NN},
+
+// DS.T_NNt // DS.T_NNt
+new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.ERROR, DS.T_S, DS.T_NNt, DS.ERROR, DS.ERROR, DS.ERROR, DS.T_NNt, DS.T_NNt, DS.TX_NNN},
+};
+ // End NumEnd NumAmPm NumSpace NumDaySep NumTimesep MonthEnd MonthSpace MonthDSep NumDateSuff NumTimeSuff DayOfWeek YearSpace YearDateSep YearEnd TimeZone Era UTCMark
+
+ internal const String GMTName = "GMT";
+ internal const String ZuluName = "Z";
+
+ //
+ // Search from the index of str at str.Index to see if the target string exists in the str.
+ //
+ private static bool MatchWord(ref __DTString str, String target)
+ {
+ int length = target.Length;
+ if (length > (str.Value.Length - str.Index))
+ {
+ return false;
+ }
+
+ if (str.CompareInfo.Compare(str.Value, str.Index, length,
+ target, 0, length, CompareOptions.IgnoreCase) != 0)
+ {
+ return (false);
+ }
+
+ int nextCharIndex = str.Index + target.Length;
+
+ if (nextCharIndex < str.Value.Length)
+ {
+ char nextCh = str.Value[nextCharIndex];
+ if (Char.IsLetter(nextCh))
+ {
+ return (false);
+ }
+ }
+ str.Index = nextCharIndex;
+ if (str.Index < str.len)
+ {
+ str.m_current = str.Value[str.Index];
+ }
+
+ return (true);
+ }
+
+
+ //
+ // Check the word at the current index to see if it matches GMT name or Zulu name.
+ //
+ private static bool GetTimeZoneName(ref __DTString str)
+ {
+ if (MatchWord(ref str, GMTName))
+ {
+ return (true);
+ }
+
+ if (MatchWord(ref str, ZuluName))
+ {
+ return (true);
+ }
+
+ return (false);
+ }
+
+ internal static bool IsDigit(char ch)
+ {
+ return (ch >= '0' && ch <= '9');
+ }
+
+
+ /*=================================ParseFraction==========================
+ **Action: Starting at the str.Index, which should be a decimal symbol.
+ ** if the current character is a digit, parse the remaining
+ ** numbers as fraction. For example, if the sub-string starting at str.Index is "123", then
+ ** the method will return 0.123
+ **Returns: The fraction number.
+ **Arguments:
+ ** str the parsing string
+ **Exceptions:
+ ============================================================================*/
+
+ private static bool ParseFraction(ref __DTString str, out double result)
+ {
+ result = 0;
+ double decimalBase = 0.1;
+ int digits = 0;
+ char ch;
+ while (str.GetNext()
+ && IsDigit(ch = str.m_current))
+ {
+ result += (ch - '0') * decimalBase;
+ decimalBase *= 0.1;
+ digits++;
+ }
+ return (digits > 0);
+ }
+
+ /*=================================ParseTimeZone==========================
+ **Action: Parse the timezone offset in the following format:
+ ** "+8", "+08", "+0800", "+0800"
+ ** This method is used by DateTime.Parse().
+ **Returns: The TimeZone offset.
+ **Arguments:
+ ** str the parsing string
+ **Exceptions:
+ ** FormatException if invalid timezone format is found.
+ ============================================================================*/
+
+ private static bool ParseTimeZone(ref __DTString str, ref TimeSpan result)
+ {
+ // The hour/minute offset for timezone.
+ int hourOffset = 0;
+ int minuteOffset = 0;
+ DTSubString sub;
+
+ // Consume the +/- character that has already been read
+ sub = str.GetSubString();
+ if (sub.length != 1)
+ {
+ return false;
+ }
+ char offsetChar = sub[0];
+ if (offsetChar != '+' && offsetChar != '-')
+ {
+ return false;
+ }
+ str.ConsumeSubString(sub);
+
+ sub = str.GetSubString();
+ if (sub.type != DTSubStringType.Number)
+ {
+ return false;
+ }
+ int value = sub.value;
+ int length = sub.length;
+ if (length == 1 || length == 2)
+ {
+ // Parsing "+8" or "+08"
+ hourOffset = value;
+ str.ConsumeSubString(sub);
+ // See if we have minutes
+ sub = str.GetSubString();
+ if (sub.length == 1 && sub[0] == ':')
+ {
+ // Parsing "+8:00" or "+08:00"
+ str.ConsumeSubString(sub);
+ sub = str.GetSubString();
+ if (sub.type != DTSubStringType.Number || sub.length < 1 || sub.length > 2)
+ {
+ return false;
+ }
+ minuteOffset = sub.value;
+ str.ConsumeSubString(sub);
+ }
+ }
+ else if (length == 3 || length == 4)
+ {
+ // Parsing "+800" or "+0800"
+ hourOffset = value / 100;
+ minuteOffset = value % 100;
+ str.ConsumeSubString(sub);
+ }
+ else
+ {
+ // Wrong number of digits
+ return false;
+ }
+ Debug.Assert(hourOffset >= 0 && hourOffset <= 99, "hourOffset >= 0 && hourOffset <= 99");
+ Debug.Assert(minuteOffset >= 0 && minuteOffset <= 99, "minuteOffset >= 0 && minuteOffset <= 99");
+ if (minuteOffset < 0 || minuteOffset >= 60)
+ {
+ return false;
+ }
+
+ result = new TimeSpan(hourOffset, minuteOffset, 0);
+ if (offsetChar == '-')
+ {
+ result = result.Negate();
+ }
+ return true;
+ }
+
+ // This is the helper function to handle timezone in string in the format like +/-0800
+ private static bool HandleTimeZone(ref __DTString str, ref DateTimeResult result)
+ {
+ if ((str.Index < str.len - 1))
+ {
+ char nextCh = str.Value[str.Index];
+ // Skip whitespace, but don't update the index unless we find a time zone marker
+ int whitespaceCount = 0;
+ while (Char.IsWhiteSpace(nextCh) && str.Index + whitespaceCount < str.len - 1)
+ {
+ whitespaceCount++;
+ nextCh = str.Value[str.Index + whitespaceCount];
+ }
+ if (nextCh == '+' || nextCh == '-')
+ {
+ str.Index += whitespaceCount;
+ if ((result.flags & ParseFlags.TimeZoneUsed) != 0)
+ {
+ // Should not have two timezone offsets.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ result.flags |= ParseFlags.TimeZoneUsed;
+ if (!ParseTimeZone(ref str, ref result.timeZoneOffset))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ //
+ // This is the lexer. Check the character at the current index, and put the found token in dtok and
+ // some raw date/time information in raw.
+ //
+ private static Boolean Lex(DS dps, ref __DTString str, ref DateTimeToken dtok, ref DateTimeRawInfo raw, ref DateTimeResult result, ref DateTimeFormatInfo dtfi, DateTimeStyles styles)
+ {
+ TokenType tokenType;
+ int tokenValue;
+ int indexBeforeSeparator;
+ char charBeforeSeparator;
+
+ TokenType sep;
+ dtok.dtt = DTT.Unk; // Assume the token is unkown.
+
+ str.GetRegularToken(out tokenType, out tokenValue, dtfi);
+
+#if _LOGGING
+ // Builds with _LOGGING defined (x86dbg, amd64chk, etc) support tracing
+ // Set the following internal-only/unsupported environment variables to enable DateTime tracing to the console:
+ //
+ // COMPlus_LogEnable=1
+ // COMPlus_LogToConsole=1
+ // COMPlus_LogLevel=9
+ // COMPlus_ManagedLogFacility=0x00001000
+ if (_tracingEnabled)
+ {
+ BCLDebug.Trace("DATETIME", "[DATETIME] Lex({0})\tpos:{1}({2}), {3}, DS.{4}", Hex(str.Value),
+ str.Index, Hex(str.m_current), tokenType, dps);
+ }
+#endif // _LOGGING
+
+ // Look at the regular token.
+ switch (tokenType)
+ {
+ case TokenType.NumberToken:
+ case TokenType.YearNumberToken:
+ if (raw.numCount == 3 || tokenValue == -1)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0010", dps);
+ return false;
+ }
+ //
+ // This is a digit.
+ //
+ // If the previous parsing state is DS.T_NNt (like 12:01), and we got another number,
+ // so we will have a terminal state DS.TX_NNN (like 12:01:02).
+ // If the previous parsing state is DS.T_Nt (like 12:), and we got another number,
+ // so we will have a terminal state DS.TX_NN (like 12:01).
+ //
+ // Look ahead to see if the following character is a decimal point or timezone offset.
+ // This enables us to parse time in the forms of:
+ // "11:22:33.1234" or "11:22:33-08".
+ if (dps == DS.T_NNt)
+ {
+ if ((str.Index < str.len - 1))
+ {
+ char nextCh = str.Value[str.Index];
+ if (nextCh == '.')
+ {
+ // While ParseFraction can fail, it just means that there were no digits after
+ // the dot. In this case ParseFraction just removes the dot. This is actually
+ // valid for cultures like Albanian, that join the time marker to the time with
+ // with a dot: e.g. "9:03.MD"
+ ParseFraction(ref str, out raw.fraction);
+ }
+ }
+ }
+ if (dps == DS.T_NNt || dps == DS.T_Nt)
+ {
+ if ((str.Index < str.len - 1))
+ {
+ if (false == HandleTimeZone(ref str, ref result))
+ {
+ LexTraceExit("0020 (value like \"12:01\" or \"12:\" followed by a non-TZ number", dps);
+ return false;
+ }
+ }
+ }
+
+ dtok.num = tokenValue;
+ if (tokenType == TokenType.YearNumberToken)
+ {
+ if (raw.year == -1)
+ {
+ raw.year = tokenValue;
+ //
+ // If we have number which has 3 or more digits (like "001" or "0001"),
+ // we assume this number is a year. Save the currnet raw.numCount in
+ // raw.year.
+ //
+ switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator))
+ {
+ case TokenType.SEP_End:
+ dtok.dtt = DTT.YearEnd;
+ break;
+ case TokenType.SEP_Am:
+ case TokenType.SEP_Pm:
+ if (raw.timeMark == TM.NotSet)
+ {
+ raw.timeMark = (sep == TokenType.SEP_Am ? TM.AM : TM.PM);
+ dtok.dtt = DTT.YearSpace;
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0030 (TM.AM/TM.PM Happened more than 1x)", dps);
+ }
+ break;
+ case TokenType.SEP_Space:
+ dtok.dtt = DTT.YearSpace;
+ break;
+ case TokenType.SEP_Date:
+ dtok.dtt = DTT.YearDateSep;
+ break;
+ case TokenType.SEP_Time:
+ if (!raw.hasSameDateAndTimeSeparators)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0040 (Invalid separator after number)", dps);
+ return false;
+ }
+
+ // we have the date and time separators are same and getting a year number, then change the token to YearDateSep as
+ // we are sure we are not parsing time.
+ dtok.dtt = DTT.YearDateSep;
+ break;
+ case TokenType.SEP_DateOrOffset:
+ // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
+ // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
+ if ((dateParsingStates[(int)dps][(int)DTT.YearDateSep] == DS.ERROR)
+ && (dateParsingStates[(int)dps][(int)DTT.YearSpace] > DS.ERROR))
+ {
+ str.Index = indexBeforeSeparator;
+ str.m_current = charBeforeSeparator;
+ dtok.dtt = DTT.YearSpace;
+ }
+ else
+ {
+ dtok.dtt = DTT.YearDateSep;
+ }
+ break;
+ case TokenType.SEP_YearSuff:
+ case TokenType.SEP_MonthSuff:
+ case TokenType.SEP_DaySuff:
+ dtok.dtt = DTT.NumDatesuff;
+ dtok.suffix = sep;
+ break;
+ case TokenType.SEP_HourSuff:
+ case TokenType.SEP_MinuteSuff:
+ case TokenType.SEP_SecondSuff:
+ dtok.dtt = DTT.NumTimesuff;
+ dtok.suffix = sep;
+ break;
+ default:
+ // Invalid separator after number number.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0040 (Invalid separator after number)", dps);
+ return false;
+ }
+ //
+ // Found the token already. Return now.
+ //
+ LexTraceExit("0050 (success)", dps);
+ return true;
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0060", dps);
+ return false;
+ }
+ switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator))
+ {
+ //
+ // Note here we check if the numCount is less than three.
+ // When we have more than three numbers, it will be caught as error in the state machine.
+ //
+ case TokenType.SEP_End:
+ dtok.dtt = DTT.NumEnd;
+ raw.AddNumber(dtok.num);
+ break;
+ case TokenType.SEP_Am:
+ case TokenType.SEP_Pm:
+ if (raw.timeMark == TM.NotSet)
+ {
+ raw.timeMark = (sep == TokenType.SEP_Am ? TM.AM : TM.PM);
+ dtok.dtt = DTT.NumAmpm;
+ // Fix AM/PM parsing case, e.g. "1/10 5 AM"
+ if (dps == DS.D_NN)
+ {
+ if (!ProcessTerminaltState(DS.DX_NN, ref result, ref styles, ref raw, dtfi))
+ {
+ return false;
+ }
+ }
+
+ raw.AddNumber(dtok.num);
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ break;
+ }
+ if (dps == DS.T_NNt || dps == DS.T_Nt)
+ {
+ if (false == HandleTimeZone(ref str, ref result))
+ {
+ LexTraceExit("0070 (HandleTimeZone returned false)", dps);
+ return false;
+ }
+ }
+ break;
+ case TokenType.SEP_Space:
+ dtok.dtt = DTT.NumSpace;
+ raw.AddNumber(dtok.num);
+ break;
+ case TokenType.SEP_Date:
+ dtok.dtt = DTT.NumDatesep;
+ raw.AddNumber(dtok.num);
+ break;
+ case TokenType.SEP_DateOrOffset:
+ // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
+ // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
+ if ((dateParsingStates[(int)dps][(int)DTT.NumDatesep] == DS.ERROR)
+ && (dateParsingStates[(int)dps][(int)DTT.NumSpace] > DS.ERROR))
+ {
+ str.Index = indexBeforeSeparator;
+ str.m_current = charBeforeSeparator;
+ dtok.dtt = DTT.NumSpace;
+ }
+ else
+ {
+ dtok.dtt = DTT.NumDatesep;
+ }
+ raw.AddNumber(dtok.num);
+ break;
+ case TokenType.SEP_Time:
+ if (raw.hasSameDateAndTimeSeparators &&
+ (dps == DS.D_Y || dps == DS.D_YN || dps == DS.D_YNd || dps == DS.D_YM || dps == DS.D_YMd))
+ {
+ // we are parsing a date and we have the time separator same as date separator, so we mark the token as date separator
+ dtok.dtt = DTT.NumDatesep;
+ raw.AddNumber(dtok.num);
+ break;
+ }
+ dtok.dtt = DTT.NumTimesep;
+ raw.AddNumber(dtok.num);
+ break;
+ case TokenType.SEP_YearSuff:
+ try
+ {
+ dtok.num = dtfi.Calendar.ToFourDigitYear(tokenValue);
+ }
+ catch (ArgumentOutOfRangeException e)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e);
+ LexTraceExit("0075 (Calendar.ToFourDigitYear failed)", dps);
+ return false;
+ }
+ dtok.dtt = DTT.NumDatesuff;
+ dtok.suffix = sep;
+ break;
+ case TokenType.SEP_MonthSuff:
+ case TokenType.SEP_DaySuff:
+ dtok.dtt = DTT.NumDatesuff;
+ dtok.suffix = sep;
+ break;
+ case TokenType.SEP_HourSuff:
+ case TokenType.SEP_MinuteSuff:
+ case TokenType.SEP_SecondSuff:
+ dtok.dtt = DTT.NumTimesuff;
+ dtok.suffix = sep;
+ break;
+ case TokenType.SEP_LocalTimeMark:
+ dtok.dtt = DTT.NumLocalTimeMark;
+ raw.AddNumber(dtok.num);
+ break;
+ default:
+ // Invalid separator after number number.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0080", dps);
+ return false;
+ }
+ break;
+ case TokenType.HebrewNumber:
+ if (tokenValue >= 100)
+ {
+ // This is a year number
+ if (raw.year == -1)
+ {
+ raw.year = tokenValue;
+ //
+ // If we have number which has 3 or more digits (like "001" or "0001"),
+ // we assume this number is a year. Save the currnet raw.numCount in
+ // raw.year.
+ //
+ switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator))
+ {
+ case TokenType.SEP_End:
+ dtok.dtt = DTT.YearEnd;
+ break;
+ case TokenType.SEP_Space:
+ dtok.dtt = DTT.YearSpace;
+ break;
+ case TokenType.SEP_DateOrOffset:
+ // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
+ // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
+ if (dateParsingStates[(int)dps][(int)DTT.YearSpace] > DS.ERROR)
+ {
+ str.Index = indexBeforeSeparator;
+ str.m_current = charBeforeSeparator;
+ dtok.dtt = DTT.YearSpace;
+ break;
+ }
+ goto default;
+ default:
+ // Invalid separator after number number.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0090", dps);
+ return false;
+ }
+ }
+ else
+ {
+ // Invalid separator after number number.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0100", dps);
+ return false;
+ }
+ }
+ else
+ {
+ // This is a day number
+ dtok.num = tokenValue;
+ raw.AddNumber(dtok.num);
+
+ switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator))
+ {
+ //
+ // Note here we check if the numCount is less than three.
+ // When we have more than three numbers, it will be caught as error in the state machine.
+ //
+ case TokenType.SEP_End:
+ dtok.dtt = DTT.NumEnd;
+ break;
+ case TokenType.SEP_Space:
+ case TokenType.SEP_Date:
+ dtok.dtt = DTT.NumDatesep;
+ break;
+ case TokenType.SEP_DateOrOffset:
+ // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
+ // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
+ if ((dateParsingStates[(int)dps][(int)DTT.NumDatesep] == DS.ERROR)
+ && (dateParsingStates[(int)dps][(int)DTT.NumSpace] > DS.ERROR))
+ {
+ str.Index = indexBeforeSeparator;
+ str.m_current = charBeforeSeparator;
+ dtok.dtt = DTT.NumSpace;
+ }
+ else
+ {
+ dtok.dtt = DTT.NumDatesep;
+ }
+ break;
+ default:
+ // Invalid separator after number number.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0110", dps);
+ return false;
+ }
+ }
+ break;
+ case TokenType.DayOfWeekToken:
+ if (raw.dayOfWeek == -1)
+ {
+ //
+ // This is a day of week name.
+ //
+ raw.dayOfWeek = tokenValue;
+ dtok.dtt = DTT.DayOfWeek;
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0120 (DayOfWeek seen more than 1x)", dps);
+ return false;
+ }
+ break;
+ case TokenType.MonthToken:
+ if (raw.month == -1)
+ {
+ //
+ // This is a month name
+ //
+ switch (sep = str.GetSeparatorToken(dtfi, out indexBeforeSeparator, out charBeforeSeparator))
+ {
+ case TokenType.SEP_End:
+ dtok.dtt = DTT.MonthEnd;
+ break;
+ case TokenType.SEP_Space:
+ dtok.dtt = DTT.MonthSpace;
+ break;
+ case TokenType.SEP_Date:
+ dtok.dtt = DTT.MonthDatesep;
+ break;
+ case TokenType.SEP_Time:
+ if (!raw.hasSameDateAndTimeSeparators)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0130 (Invalid separator after month name)", dps);
+ return false;
+ }
+
+ // we have the date and time separators are same and getting a Month name, then change the token to MonthDatesep as
+ // we are sure we are not parsing time.
+ dtok.dtt = DTT.MonthDatesep;
+ break;
+ case TokenType.SEP_DateOrOffset:
+ // The separator is either a date separator or the start of a time zone offset. If the token will complete the date then
+ // process just the number and roll back the index so that the outer loop can attempt to parse the time zone offset.
+ if ((dateParsingStates[(int)dps][(int)DTT.MonthDatesep] == DS.ERROR)
+ && (dateParsingStates[(int)dps][(int)DTT.MonthSpace] > DS.ERROR))
+ {
+ str.Index = indexBeforeSeparator;
+ str.m_current = charBeforeSeparator;
+ dtok.dtt = DTT.MonthSpace;
+ }
+ else
+ {
+ dtok.dtt = DTT.MonthDatesep;
+ }
+ break;
+ default:
+ //Invalid separator after month name
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0130 (Invalid separator after month name)", dps);
+ return false;
+ }
+ raw.month = tokenValue;
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0140 (MonthToken seen more than 1x)", dps);
+ return false;
+ }
+ break;
+ case TokenType.EraToken:
+ if (result.era != -1)
+ {
+ result.era = tokenValue;
+ dtok.dtt = DTT.Era;
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0150 (EraToken seen when result.era already set)", dps);
+ return false;
+ }
+ break;
+ case TokenType.JapaneseEraToken:
+ // Special case for Japanese. We allow Japanese era name to be used even if the calendar is not Japanese Calendar.
+ result.calendar = JapaneseCalendar.GetDefaultInstance();
+ dtfi = DateTimeFormatInfo.GetJapaneseCalendarDTFI();
+ if (result.era != -1)
+ {
+ result.era = tokenValue;
+ dtok.dtt = DTT.Era;
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0160 (JapaneseEraToken seen when result.era already set)", dps);
+ return false;
+ }
+ break;
+ case TokenType.TEraToken:
+ result.calendar = TaiwanCalendar.GetDefaultInstance();
+ dtfi = DateTimeFormatInfo.GetTaiwanCalendarDTFI();
+ if (result.era != -1)
+ {
+ result.era = tokenValue;
+ dtok.dtt = DTT.Era;
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0170 (TEraToken seen when result.era already set)", dps);
+ return false;
+ }
+ break;
+ case TokenType.TimeZoneToken:
+ //
+ // This is a timezone designator
+ //
+ // NOTENOTE : for now, we only support "GMT" and "Z" (for Zulu time).
+ //
+ if ((result.flags & ParseFlags.TimeZoneUsed) != 0)
+ {
+ // Should not have two timezone offsets.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0180 (seen GMT or Z more than 1x)", dps);
+ return false;
+ }
+ dtok.dtt = DTT.TimeZone;
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = new TimeSpan(0);
+ result.flags |= ParseFlags.TimeZoneUtc;
+ break;
+ case TokenType.EndOfString:
+ dtok.dtt = DTT.End;
+ break;
+ case TokenType.DateWordToken:
+ case TokenType.IgnorableSymbol:
+ // Date words and ignorable symbols can just be skipped over
+ break;
+ case TokenType.Am:
+ case TokenType.Pm:
+ if (raw.timeMark == TM.NotSet)
+ {
+ raw.timeMark = (TM)tokenValue;
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0190 (AM/PM timeMark already set)", dps);
+ return false;
+ }
+ break;
+ case TokenType.UnknownToken:
+ if (Char.IsLetter(str.m_current))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_UnknowDateTimeWord", str.Index);
+ LexTraceExit("0200", dps);
+ return (false);
+ }
+
+ if ((str.m_current == '-' || str.m_current == '+') && ((result.flags & ParseFlags.TimeZoneUsed) == 0))
+ {
+ Int32 originalIndex = str.Index;
+ if (ParseTimeZone(ref str, ref result.timeZoneOffset))
+ {
+ result.flags |= ParseFlags.TimeZoneUsed;
+ LexTraceExit("0220 (success)", dps);
+ return true;
+ }
+ else
+ {
+ // Time zone parse attempt failed. Fall through to punctuation handling.
+ str.Index = originalIndex;
+ }
+ }
+
+ // Visual Basic implements string to date conversions on top of DateTime.Parse:
+ // CDate("#10/10/95#")
+ //
+ if (VerifyValidPunctuation(ref str))
+ {
+ LexTraceExit("0230 (success)", dps);
+ return true;
+ }
+
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ LexTraceExit("0240", dps);
+ return false;
+ }
+
+ LexTraceExit("0250 (success)", dps);
+ return true;
+ }
+
+ private static Boolean VerifyValidPunctuation(ref __DTString str)
+ {
+ // Compatability Behavior. Allow trailing nulls and surrounding hashes
+ Char ch = str.Value[str.Index];
+ if (ch == '#')
+ {
+ bool foundStart = false;
+ bool foundEnd = false;
+ for (int i = 0; i < str.len; i++)
+ {
+ ch = str.Value[i];
+ if (ch == '#')
+ {
+ if (foundStart)
+ {
+ if (foundEnd)
+ {
+ // Having more than two hashes is invalid
+ return false;
+ }
+ else
+ {
+ foundEnd = true;
+ }
+ }
+ else
+ {
+ foundStart = true;
+ }
+ }
+ else if (ch == '\0')
+ {
+ // Allow nulls only at the end
+ if (!foundEnd)
+ {
+ return false;
+ }
+ }
+ else if ((!Char.IsWhiteSpace(ch)))
+ {
+ // Anthyhing other than whitespace outside hashes is invalid
+ if (!foundStart || foundEnd)
+ {
+ return false;
+ }
+ }
+ }
+ if (!foundEnd)
+ {
+ // The has was un-paired
+ return false;
+ }
+ // Valid Hash usage: eat the hash and continue.
+ str.GetNext();
+ return true;
+ }
+ else if (ch == '\0')
+ {
+ for (int i = str.Index; i < str.len; i++)
+ {
+ if (str.Value[i] != '\0')
+ {
+ // Nulls are only valid if they are the only trailing character
+ return false;
+ }
+ }
+ // Move to the end of the string
+ str.Index = str.len;
+ return true;
+ }
+ return false;
+ }
+
+ private const int ORDER_YMD = 0; // The order of date is Year/Month/Day.
+ private const int ORDER_MDY = 1; // The order of date is Month/Day/Year.
+ private const int ORDER_DMY = 2; // The order of date is Day/Month/Year.
+ private const int ORDER_YDM = 3; // The order of date is Year/Day/Month
+ private const int ORDER_YM = 4; // Year/Month order.
+ private const int ORDER_MY = 5; // Month/Year order.
+ private const int ORDER_MD = 6; // Month/Day order.
+ private const int ORDER_DM = 7; // Day/Month order.
+
+ //
+ // Decide the year/month/day order from the datePattern.
+ //
+ // Return 0 for YMD, 1 for MDY, 2 for DMY, otherwise -1.
+ //
+ private static Boolean GetYearMonthDayOrder(String datePattern, DateTimeFormatInfo dtfi, out int order)
+ {
+ int yearOrder = -1;
+ int monthOrder = -1;
+ int dayOrder = -1;
+ int orderCount = 0;
+
+ bool inQuote = false;
+
+ for (int i = 0; i < datePattern.Length && orderCount < 3; i++)
+ {
+ char ch = datePattern[i];
+ if (ch == '\\' || ch == '%')
+ {
+ i++;
+ continue; // Skip next character that is escaped by this backslash
+ }
+
+ if (ch == '\'' || ch == '"')
+ {
+ inQuote = !inQuote;
+ }
+
+ if (!inQuote)
+ {
+ if (ch == 'y')
+ {
+ yearOrder = orderCount++;
+
+ //
+ // Skip all year pattern charaters.
+ //
+ for (; i + 1 < datePattern.Length && datePattern[i + 1] == 'y'; i++)
+ {
+ // Do nothing here.
+ }
+ }
+ else if (ch == 'M')
+ {
+ monthOrder = orderCount++;
+ //
+ // Skip all month pattern characters.
+ //
+ for (; i + 1 < datePattern.Length && datePattern[i + 1] == 'M'; i++)
+ {
+ // Do nothing here.
+ }
+ }
+ else if (ch == 'd')
+ {
+ int patternCount = 1;
+ //
+ // Skip all day pattern characters.
+ //
+ for (; i + 1 < datePattern.Length && datePattern[i + 1] == 'd'; i++)
+ {
+ patternCount++;
+ }
+ //
+ // Make sure this is not "ddd" or "dddd", which means day of week.
+ //
+ if (patternCount <= 2)
+ {
+ dayOrder = orderCount++;
+ }
+ }
+ }
+ }
+
+ if (yearOrder == 0 && monthOrder == 1 && dayOrder == 2)
+ {
+ order = ORDER_YMD;
+ return true;
+ }
+ if (monthOrder == 0 && dayOrder == 1 && yearOrder == 2)
+ {
+ order = ORDER_MDY;
+ return true;
+ }
+ if (dayOrder == 0 && monthOrder == 1 && yearOrder == 2)
+ {
+ order = ORDER_DMY;
+ return true;
+ }
+ if (yearOrder == 0 && dayOrder == 1 && monthOrder == 2)
+ {
+ order = ORDER_YDM;
+ return true;
+ }
+ order = -1;
+ return false;
+ }
+
+ //
+ // Decide the year/month order from the pattern.
+ //
+ // Return 0 for YM, 1 for MY, otherwise -1.
+ //
+ private static Boolean GetYearMonthOrder(String pattern, DateTimeFormatInfo dtfi, out int order)
+ {
+ int yearOrder = -1;
+ int monthOrder = -1;
+ int orderCount = 0;
+
+ bool inQuote = false;
+ for (int i = 0; i < pattern.Length && orderCount < 2; i++)
+ {
+ char ch = pattern[i];
+ if (ch == '\\' || ch == '%')
+ {
+ i++;
+ continue; // Skip next character that is escaped by this backslash
+ }
+
+ if (ch == '\'' || ch == '"')
+ {
+ inQuote = !inQuote;
+ }
+
+ if (!inQuote)
+ {
+ if (ch == 'y')
+ {
+ yearOrder = orderCount++;
+
+ //
+ // Skip all year pattern charaters.
+ //
+ for (; i + 1 < pattern.Length && pattern[i + 1] == 'y'; i++)
+ {
+ }
+ }
+ else if (ch == 'M')
+ {
+ monthOrder = orderCount++;
+ //
+ // Skip all month pattern characters.
+ //
+ for (; i + 1 < pattern.Length && pattern[i + 1] == 'M'; i++)
+ {
+ }
+ }
+ }
+ }
+
+ if (yearOrder == 0 && monthOrder == 1)
+ {
+ order = ORDER_YM;
+ return true;
+ }
+ if (monthOrder == 0 && yearOrder == 1)
+ {
+ order = ORDER_MY;
+ return true;
+ }
+ order = -1;
+ return false;
+ }
+
+ //
+ // Decide the month/day order from the pattern.
+ //
+ // Return 0 for MD, 1 for DM, otherwise -1.
+ //
+ private static Boolean GetMonthDayOrder(String pattern, DateTimeFormatInfo dtfi, out int order)
+ {
+ int monthOrder = -1;
+ int dayOrder = -1;
+ int orderCount = 0;
+
+ bool inQuote = false;
+ for (int i = 0; i < pattern.Length && orderCount < 2; i++)
+ {
+ char ch = pattern[i];
+ if (ch == '\\' || ch == '%')
+ {
+ i++;
+ continue; // Skip next character that is escaped by this backslash
+ }
+
+ if (ch == '\'' || ch == '"')
+ {
+ inQuote = !inQuote;
+ }
+
+ if (!inQuote)
+ {
+ if (ch == 'd')
+ {
+ int patternCount = 1;
+ //
+ // Skip all day pattern charaters.
+ //
+ for (; i + 1 < pattern.Length && pattern[i + 1] == 'd'; i++)
+ {
+ patternCount++;
+ }
+
+ //
+ // Make sure this is not "ddd" or "dddd", which means day of week.
+ //
+ if (patternCount <= 2)
+ {
+ dayOrder = orderCount++;
+ }
+ }
+ else if (ch == 'M')
+ {
+ monthOrder = orderCount++;
+ //
+ // Skip all month pattern characters.
+ //
+ for (; i + 1 < pattern.Length && pattern[i + 1] == 'M'; i++)
+ {
+ }
+ }
+ }
+ }
+
+ if (monthOrder == 0 && dayOrder == 1)
+ {
+ order = ORDER_MD;
+ return true;
+ }
+ if (dayOrder == 0 && monthOrder == 1)
+ {
+ order = ORDER_DM;
+ return true;
+ }
+ order = -1;
+ return false;
+ }
+
+ //
+ // Adjust the two-digit year if necessary.
+ //
+ private static bool TryAdjustYear(ref DateTimeResult result, int year, out int adjustedYear)
+ {
+ if (year < 100)
+ {
+ try
+ {
+ // the Calendar classes need some real work. Many of the calendars that throw
+ // don't implement a fast/non-allocating (and non-throwing) IsValid{Year|Day|Month} method.
+ // we are making a targeted try/catch fix in the in-place release but will revisit this code
+ // in the next side-by-side release.
+ year = result.calendar.ToFourDigitYear(year);
+ }
+ catch (ArgumentOutOfRangeException)
+ {
+ adjustedYear = -1;
+ return false;
+ }
+ }
+ adjustedYear = year;
+ return true;
+ }
+
+ private static bool SetDateYMD(ref DateTimeResult result, int year, int month, int day)
+ {
+ // Note, longer term these checks should be done at the end of the parse. This current
+ // way of checking creates order dependence with parsing the era name.
+ if (result.calendar.IsValidDay(year, month, day, result.era))
+ {
+ result.SetDate(year, month, day); // YMD
+ return (true);
+ }
+ return (false);
+ }
+
+ private static bool SetDateMDY(ref DateTimeResult result, int month, int day, int year)
+ {
+ return (SetDateYMD(ref result, year, month, day));
+ }
+
+ private static bool SetDateDMY(ref DateTimeResult result, int day, int month, int year)
+ {
+ return (SetDateYMD(ref result, year, month, day));
+ }
+
+ private static bool SetDateYDM(ref DateTimeResult result, int year, int day, int month)
+ {
+ return (SetDateYMD(ref result, year, month, day));
+ }
+
+ private static void GetDefaultYear(ref DateTimeResult result, ref DateTimeStyles styles)
+ {
+ result.Year = result.calendar.GetYear(GetDateTimeNow(ref result, ref styles));
+ result.flags |= ParseFlags.YearDefault;
+ }
+
+ // Processing teriminal case: DS.DX_NN
+ private static Boolean GetDayOfNN(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ int n1 = raw.GetNumber(0);
+ int n2 = raw.GetNumber(1);
+
+ GetDefaultYear(ref result, ref styles);
+
+ int order;
+ if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out order))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
+ return false;
+ }
+
+ if (order == ORDER_MD)
+ {
+ if (SetDateYMD(ref result, result.Year, n1, n2)) // MD
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ }
+ else
+ {
+ // ORDER_DM
+ if (SetDateYMD(ref result, result.Year, n2, n1)) // DM
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ // Processing teriminal case: DS.DX_NNN
+ private static Boolean GetDayOfNNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ int n1 = raw.GetNumber(0);
+ int n2 = raw.GetNumber(1); ;
+ int n3 = raw.GetNumber(2);
+
+ int order;
+ if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
+ return false;
+ }
+ int year;
+
+ if (order == ORDER_YMD)
+ {
+ if (TryAdjustYear(ref result, n1, out year) && SetDateYMD(ref result, year, n2, n3)) // YMD
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ }
+ else if (order == ORDER_MDY)
+ {
+ if (TryAdjustYear(ref result, n3, out year) && SetDateMDY(ref result, n1, n2, year)) // MDY
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ }
+ else if (order == ORDER_DMY)
+ {
+ if (TryAdjustYear(ref result, n3, out year) && SetDateDMY(ref result, n1, n2, year)) // DMY
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ }
+ else if (order == ORDER_YDM)
+ {
+ if (TryAdjustYear(ref result, n1, out year) && SetDateYDM(ref result, year, n2, n3)) // YDM
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ private static Boolean GetDayOfMN(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ // The interpretation is based on the MonthDayPattern and YearMonthPattern
+ //
+ // MonthDayPattern YearMonthPattern Interpretation
+ // --------------- ---------------- ---------------
+ // MMMM dd MMMM yyyy Day
+ // MMMM dd yyyy MMMM Day
+ // dd MMMM MMMM yyyy Year
+ // dd MMMM yyyy MMMM Day
+ //
+ // In the first and last cases, it could be either or neither, but a day is a better default interpretation
+ // than a 2 digit year.
+
+ int monthDayOrder;
+ if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
+ return false;
+ }
+ if (monthDayOrder == ORDER_DM)
+ {
+ int yearMonthOrder;
+ if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern);
+ return false;
+ }
+ if (yearMonthOrder == ORDER_MY)
+ {
+ int year;
+ if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ GetDefaultYear(ref result, ref styles);
+ if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0)))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Actions:
+ // Deal with the terminal state for Hebrew Month/Day pattern
+ //
+ ////////////////////////////////////////////////////////////////////////
+
+ private static Boolean GetHebrewDayOfNM(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ int monthDayOrder;
+ if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
+ return false;
+ }
+ result.Month = raw.month;
+ if (monthDayOrder == ORDER_DM || monthDayOrder == ORDER_MD)
+ {
+ if (result.calendar.IsValidDay(result.Year, result.Month, raw.GetNumber(0), result.era))
+ {
+ result.Day = raw.GetNumber(0);
+ return true;
+ }
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ private static Boolean GetDayOfNM(ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ // The interpretation is based on the MonthDayPattern and YearMonthPattern
+ //
+ // MonthDayPattern YearMonthPattern Interpretation
+ // --------------- ---------------- ---------------
+ // MMMM dd MMMM yyyy Day
+ // MMMM dd yyyy MMMM Year
+ // dd MMMM MMMM yyyy Day
+ // dd MMMM yyyy MMMM Day
+ //
+ // In the first and last cases, it could be either or neither, but a day is a better default interpretation
+ // than a 2 digit year.
+
+ int monthDayOrder;
+ if (!GetMonthDayOrder(dtfi.MonthDayPattern, dtfi, out monthDayOrder))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.MonthDayPattern);
+ return false;
+ }
+ if (monthDayOrder == ORDER_MD)
+ {
+ int yearMonthOrder;
+ if (!GetYearMonthOrder(dtfi.YearMonthPattern, dtfi, out yearMonthOrder))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.YearMonthPattern);
+ return false;
+ }
+ if (yearMonthOrder == ORDER_YM)
+ {
+ int year;
+ if (!TryAdjustYear(ref result, raw.GetNumber(0), out year) || !SetDateYMD(ref result, year, raw.month, 1))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ GetDefaultYear(ref result, ref styles);
+ if (!SetDateYMD(ref result, result.Year, raw.month, raw.GetNumber(0)))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ return true;
+ }
+
+ private static Boolean GetDayOfMNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ int n1 = raw.GetNumber(0);
+ int n2 = raw.GetNumber(1);
+
+ int order;
+ if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
+ return false;
+ }
+ int year;
+
+ if (order == ORDER_MDY)
+ {
+ if (TryAdjustYear(ref result, n2, out year) && result.calendar.IsValidDay(year, raw.month, n1, result.era))
+ {
+ result.SetDate(year, raw.month, n1); // MDY
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ else if (TryAdjustYear(ref result, n1, out year) && result.calendar.IsValidDay(year, raw.month, n2, result.era))
+ {
+ result.SetDate(year, raw.month, n2); // YMD
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ }
+ else if (order == ORDER_YMD)
+ {
+ if (TryAdjustYear(ref result, n1, out year) && result.calendar.IsValidDay(year, raw.month, n2, result.era))
+ {
+ result.SetDate(year, raw.month, n2); // YMD
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ else if (TryAdjustYear(ref result, n2, out year) && result.calendar.IsValidDay(year, raw.month, n1, result.era))
+ {
+ result.SetDate(year, raw.month, n1); // DMY
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ }
+ else if (order == ORDER_DMY)
+ {
+ if (TryAdjustYear(ref result, n2, out year) && result.calendar.IsValidDay(year, raw.month, n1, result.era))
+ {
+ result.SetDate(year, raw.month, n1); // DMY
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ else if (TryAdjustYear(ref result, n1, out year) && result.calendar.IsValidDay(year, raw.month, n2, result.era))
+ {
+ result.SetDate(year, raw.month, n2); // YMD
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ }
+
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ private static Boolean GetDayOfYNN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ int n1 = raw.GetNumber(0);
+ int n2 = raw.GetNumber(1);
+ String pattern = dtfi.ShortDatePattern;
+
+ // For compatibility, don't throw if we can't determine the order, but default to YMD instead
+ int order;
+ if (GetYearMonthDayOrder(pattern, dtfi, out order) && order == ORDER_YDM)
+ {
+ if (SetDateYMD(ref result, raw.year, n2, n1))
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true; // Year + DM
+ }
+ }
+ else
+ {
+ if (SetDateYMD(ref result, raw.year, n1, n2))
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true; // Year + MD
+ }
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ private static Boolean GetDayOfNNY(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ int n1 = raw.GetNumber(0);
+ int n2 = raw.GetNumber(1);
+
+ int order;
+ if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
+ return false;
+ }
+
+ if (order == ORDER_MDY || order == ORDER_YMD)
+ {
+ if (SetDateYMD(ref result, raw.year, n1, n2))
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true; // MD + Year
+ }
+ }
+ else
+ {
+ if (SetDateYMD(ref result, raw.year, n2, n1))
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true; // DM + Year
+ }
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+
+ private static Boolean GetDayOfYMN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ if (SetDateYMD(ref result, raw.year, raw.month, raw.GetNumber(0)))
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ private static Boolean GetDayOfYN(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ if (SetDateYMD(ref result, raw.year, raw.GetNumber(0), 1))
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ private static Boolean GetDayOfYM(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ if ((result.flags & ParseFlags.HaveDate) != 0)
+ {
+ // Multiple dates in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ if (SetDateYMD(ref result, raw.year, raw.month, 1))
+ {
+ result.flags |= ParseFlags.HaveDate;
+ return true;
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ private static void AdjustTimeMark(DateTimeFormatInfo dtfi, ref DateTimeRawInfo raw)
+ {
+ // Specail case for culture which uses AM as empty string.
+ // E.g. af-ZA (0x0436)
+ // S1159 \x0000
+ // S2359 nm
+ // In this case, if we are parsing a string like "2005/09/14 12:23", we will assume this is in AM.
+
+ if (raw.timeMark == TM.NotSet)
+ {
+ if (dtfi.AMDesignator != null && dtfi.PMDesignator != null)
+ {
+ if (dtfi.AMDesignator.Length == 0 && dtfi.PMDesignator.Length != 0)
+ {
+ raw.timeMark = TM.AM;
+ }
+ if (dtfi.PMDesignator.Length == 0 && dtfi.AMDesignator.Length != 0)
+ {
+ raw.timeMark = TM.PM;
+ }
+ }
+ }
+ }
+
+ //
+ // Adjust hour according to the time mark.
+ //
+ private static Boolean AdjustHour(ref int hour, TM timeMark)
+ {
+ if (timeMark != TM.NotSet)
+ {
+ if (timeMark == TM.AM)
+ {
+ if (hour < 0 || hour > 12)
+ {
+ return false;
+ }
+ hour = (hour == 12) ? 0 : hour;
+ }
+ else
+ {
+ if (hour < 0 || hour > 23)
+ {
+ return false;
+ }
+ if (hour < 12)
+ {
+ hour += 12;
+ }
+ }
+ }
+ return true;
+ }
+
+ private static Boolean GetTimeOfN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw)
+ {
+ if ((result.flags & ParseFlags.HaveTime) != 0)
+ {
+ // Multiple times in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ //
+ // In this case, we need a time mark. Check if so.
+ //
+ if (raw.timeMark == TM.NotSet)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ result.Hour = raw.GetNumber(0);
+ result.flags |= ParseFlags.HaveTime;
+ return true;
+ }
+
+ private static Boolean GetTimeOfNN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw)
+ {
+ Debug.Assert(raw.numCount >= 2, "raw.numCount >= 2");
+ if ((result.flags & ParseFlags.HaveTime) != 0)
+ {
+ // Multiple times in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ result.Hour = raw.GetNumber(0);
+ result.Minute = raw.GetNumber(1);
+ result.flags |= ParseFlags.HaveTime;
+ return true;
+ }
+
+ private static Boolean GetTimeOfNNN(DateTimeFormatInfo dtfi, ref DateTimeResult result, ref DateTimeRawInfo raw)
+ {
+ if ((result.flags & ParseFlags.HaveTime) != 0)
+ {
+ // Multiple times in the input string
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ Debug.Assert(raw.numCount >= 3, "raw.numCount >= 3");
+ result.Hour = raw.GetNumber(0);
+ result.Minute = raw.GetNumber(1);
+ result.Second = raw.GetNumber(2);
+ result.flags |= ParseFlags.HaveTime;
+ return true;
+ }
+
+ //
+ // Processing terminal state: A Date suffix followed by one number.
+ //
+ private static Boolean GetDateOfDSN(ref DateTimeResult result, ref DateTimeRawInfo raw)
+ {
+ if (raw.numCount != 1 || result.Day != -1)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ result.Day = raw.GetNumber(0);
+ return true;
+ }
+
+ private static Boolean GetDateOfNDS(ref DateTimeResult result, ref DateTimeRawInfo raw)
+ {
+ if (result.Month == -1)
+ {
+ //Should have a month suffix
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ if (result.Year != -1)
+ {
+ // Aleady has a year suffix
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ if (!TryAdjustYear(ref result, raw.GetNumber(0), out result.Year))
+ {
+ // the year value is out of range
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ result.Day = 1;
+ return true;
+ }
+
+ private static Boolean GetDateOfNNDS(ref DateTimeResult result, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ // For partial CJK Dates, the only valid formats are with a specified year, followed by two numbers, which
+ // will be the Month and Day, and with a specified Month, when the numbers are either the year and day or
+ // day and year, depending on the short date pattern.
+
+ if ((result.flags & ParseFlags.HaveYear) != 0)
+ {
+ if (((result.flags & ParseFlags.HaveMonth) == 0) && ((result.flags & ParseFlags.HaveDay) == 0))
+ {
+ if (TryAdjustYear(ref result, raw.year, out result.Year) && SetDateYMD(ref result, result.Year, raw.GetNumber(0), raw.GetNumber(1)))
+ {
+ return true;
+ }
+ }
+ }
+ else if ((result.flags & ParseFlags.HaveMonth) != 0)
+ {
+ if (((result.flags & ParseFlags.HaveYear) == 0) && ((result.flags & ParseFlags.HaveDay) == 0))
+ {
+ int order;
+ if (!GetYearMonthDayOrder(dtfi.ShortDatePattern, dtfi, out order))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadDatePattern", dtfi.ShortDatePattern);
+ return false;
+ }
+ int year;
+ if (order == ORDER_YMD)
+ {
+ if (TryAdjustYear(ref result, raw.GetNumber(0), out year) && SetDateYMD(ref result, year, result.Month, raw.GetNumber(1)))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (TryAdjustYear(ref result, raw.GetNumber(1), out year) && SetDateYMD(ref result, year, result.Month, raw.GetNumber(0)))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ //
+ // A date suffix is found, use this method to put the number into the result.
+ //
+ private static bool ProcessDateTimeSuffix(ref DateTimeResult result, ref DateTimeRawInfo raw, ref DateTimeToken dtok)
+ {
+ switch (dtok.suffix)
+ {
+ case TokenType.SEP_YearSuff:
+ if ((result.flags & ParseFlags.HaveYear) != 0)
+ {
+ return false;
+ }
+ result.flags |= ParseFlags.HaveYear;
+ result.Year = raw.year = dtok.num;
+ break;
+ case TokenType.SEP_MonthSuff:
+ if ((result.flags & ParseFlags.HaveMonth) != 0)
+ {
+ return false;
+ }
+ result.flags |= ParseFlags.HaveMonth;
+ result.Month = raw.month = dtok.num;
+ break;
+ case TokenType.SEP_DaySuff:
+ if ((result.flags & ParseFlags.HaveDay) != 0)
+ {
+ return false;
+ }
+ result.flags |= ParseFlags.HaveDay;
+ result.Day = dtok.num;
+ break;
+ case TokenType.SEP_HourSuff:
+ if ((result.flags & ParseFlags.HaveHour) != 0)
+ {
+ return false;
+ }
+ result.flags |= ParseFlags.HaveHour;
+ result.Hour = dtok.num;
+ break;
+ case TokenType.SEP_MinuteSuff:
+ if ((result.flags & ParseFlags.HaveMinute) != 0)
+ {
+ return false;
+ }
+ result.flags |= ParseFlags.HaveMinute;
+ result.Minute = dtok.num;
+ break;
+ case TokenType.SEP_SecondSuff:
+ if ((result.flags & ParseFlags.HaveSecond) != 0)
+ {
+ return false;
+ }
+ result.flags |= ParseFlags.HaveSecond;
+ result.Second = dtok.num;
+ break;
+ }
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Actions:
+ // This is used by DateTime.Parse().
+ // Process the terminal state for the Hebrew calendar parsing.
+ //
+ ////////////////////////////////////////////////////////////////////////
+
+ internal static Boolean ProcessHebrewTerminalState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ // The following are accepted terminal state for Hebrew date.
+ switch (dps)
+ {
+ case DS.DX_MNN:
+ // Deal with the default long/short date format when the year number is ambigous (i.e. year < 100).
+ raw.year = raw.GetNumber(1);
+ if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true))
+ {
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ return false;
+ }
+ if (!GetDayOfMNN(ref result, ref raw, dtfi))
+ {
+ return false;
+ }
+ break;
+ case DS.DX_YMN:
+ // Deal with the default long/short date format when the year number is NOT ambigous (i.e. year >= 100).
+ if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true))
+ {
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ return false;
+ }
+ if (!GetDayOfYMN(ref result, ref raw, dtfi))
+ {
+ return false;
+ }
+ break;
+ case DS.DX_NM:
+ case DS.DX_MN:
+ // Deal with Month/Day pattern.
+ GetDefaultYear(ref result, ref styles);
+ if (!dtfi.YearMonthAdjustment(ref result.Year, ref raw.month, true))
+ {
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ return false;
+ }
+ if (!GetHebrewDayOfNM(ref result, ref raw, dtfi))
+ {
+ return false;
+ }
+ break;
+ case DS.DX_YM:
+ // Deal with Year/Month pattern.
+ if (!dtfi.YearMonthAdjustment(ref raw.year, ref raw.month, true))
+ {
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ return false;
+ }
+ if (!GetDayOfYM(ref result, ref raw, dtfi))
+ {
+ return false;
+ }
+ break;
+ case DS.TX_N:
+ // Deal hour + AM/PM
+ if (!GetTimeOfN(dtfi, ref result, ref raw))
+ {
+ return false;
+ }
+ break;
+ case DS.TX_NN:
+ if (!GetTimeOfNN(dtfi, ref result, ref raw))
+ {
+ return false;
+ }
+ break;
+ case DS.TX_NNN:
+ if (!GetTimeOfNNN(dtfi, ref result, ref raw))
+ {
+ return false;
+ }
+ break;
+ default:
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ if (dps > DS.ERROR)
+ {
+ //
+ // We have reached a terminal state. Reset the raw num count.
+ //
+ raw.numCount = 0;
+ }
+ return true;
+ }
+
+ //
+ // A terminal state has been reached, call the appropriate function to fill in the parsing result.
+ // Return true if the state is a terminal state.
+ //
+ internal static Boolean ProcessTerminaltState(DS dps, ref DateTimeResult result, ref DateTimeStyles styles, ref DateTimeRawInfo raw, DateTimeFormatInfo dtfi)
+ {
+ bool passed = true;
+ switch (dps)
+ {
+ case DS.DX_NN:
+ passed = GetDayOfNN(ref result, ref styles, ref raw, dtfi);
+ break;
+ case DS.DX_NNN:
+ passed = GetDayOfNNN(ref result, ref raw, dtfi);
+ break;
+ case DS.DX_MN:
+ passed = GetDayOfMN(ref result, ref styles, ref raw, dtfi);
+ break;
+ case DS.DX_NM:
+ passed = GetDayOfNM(ref result, ref styles, ref raw, dtfi);
+ break;
+ case DS.DX_MNN:
+ passed = GetDayOfMNN(ref result, ref raw, dtfi);
+ break;
+ case DS.DX_DS:
+ // The result has got the correct value. No need to process.
+ passed = true;
+ break;
+ case DS.DX_YNN:
+ passed = GetDayOfYNN(ref result, ref raw, dtfi);
+ break;
+ case DS.DX_NNY:
+ passed = GetDayOfNNY(ref result, ref raw, dtfi);
+ break;
+ case DS.DX_YMN:
+ passed = GetDayOfYMN(ref result, ref raw, dtfi);
+ break;
+ case DS.DX_YN:
+ passed = GetDayOfYN(ref result, ref raw, dtfi);
+ break;
+ case DS.DX_YM:
+ passed = GetDayOfYM(ref result, ref raw, dtfi);
+ break;
+ case DS.TX_N:
+ passed = GetTimeOfN(dtfi, ref result, ref raw);
+ break;
+ case DS.TX_NN:
+ passed = GetTimeOfNN(dtfi, ref result, ref raw);
+ break;
+ case DS.TX_NNN:
+ passed = GetTimeOfNNN(dtfi, ref result, ref raw);
+ break;
+ case DS.TX_TS:
+ // The result has got the correct value. Nothing to do.
+ passed = true;
+ break;
+ case DS.DX_DSN:
+ passed = GetDateOfDSN(ref result, ref raw);
+ break;
+ case DS.DX_NDS:
+ passed = GetDateOfNDS(ref result, ref raw);
+ break;
+ case DS.DX_NNDS:
+ passed = GetDateOfNNDS(ref result, ref raw, dtfi);
+ break;
+ }
+
+ PTSTraceExit(dps, passed);
+ if (!passed)
+ {
+ return false;
+ }
+
+ if (dps > DS.ERROR)
+ {
+ //
+ // We have reached a terminal state. Reset the raw num count.
+ //
+ raw.numCount = 0;
+ }
+ return true;
+ }
+
+ internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles)
+ {
+ DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
+ result.Init();
+ if (TryParse(s, dtfi, styles, ref result))
+ {
+ return result.parsedDate;
+ }
+ else
+ {
+ throw GetDateTimeParseException(ref result);
+ }
+ }
+
+ internal static DateTime Parse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out TimeSpan offset)
+ {
+ DateTimeResult result = new DateTimeResult(); // The buffer to store the parsing result.
+ result.Init();
+ result.flags |= ParseFlags.CaptureOffset;
+ if (TryParse(s, dtfi, styles, ref result))
+ {
+ offset = result.timeZoneOffset;
+ return result.parsedDate;
+ }
+ else
+ {
+ throw GetDateTimeParseException(ref result);
+ }
+ }
+
+
+ internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result)
+ {
+ result = DateTime.MinValue;
+ DateTimeResult resultData = new DateTimeResult(); // The buffer to store the parsing result.
+ resultData.Init();
+ if (TryParse(s, dtfi, styles, ref resultData))
+ {
+ result = resultData.parsedDate;
+ return true;
+ }
+ return false;
+ }
+
+ internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, out DateTime result, out TimeSpan offset)
+ {
+ result = DateTime.MinValue;
+ offset = TimeSpan.Zero;
+ DateTimeResult parseResult = new DateTimeResult(); // The buffer to store the parsing result.
+ parseResult.Init();
+ parseResult.flags |= ParseFlags.CaptureOffset;
+ if (TryParse(s, dtfi, styles, ref parseResult))
+ {
+ result = parseResult.parsedDate;
+ offset = parseResult.timeZoneOffset;
+ return true;
+ }
+ return false;
+ }
+
+
+ //
+ // This is the real method to do the parsing work.
+ //
+ internal static bool TryParse(String s, DateTimeFormatInfo dtfi, DateTimeStyles styles, ref DateTimeResult result)
+ {
+ if (s == null)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentNull, "ArgumentNull_String", null, nameof(s));
+ return false;
+ }
+ if (s.Length == 0)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ Debug.Assert(dtfi != null, "dtfi == null");
+
+#if _LOGGING
+ DTFITrace(dtfi);
+#endif
+
+ DateTime time;
+ //
+ // First try the predefined format.
+ //
+
+ DS dps = DS.BEGIN; // Date Parsing State.
+ bool reachTerminalState = false;
+
+ DateTimeToken dtok = new DateTimeToken(); // The buffer to store the parsing token.
+ dtok.suffix = TokenType.SEP_Unk;
+ DateTimeRawInfo raw = new DateTimeRawInfo(); // The buffer to store temporary parsing information.
+ unsafe
+ {
+ Int32* numberPointer = stackalloc Int32[3];
+ raw.Init(numberPointer);
+ }
+ raw.hasSameDateAndTimeSeparators = dtfi.DateSeparator.Equals(dtfi.TimeSeparator, StringComparison.Ordinal);
+
+ result.calendar = dtfi.Calendar;
+ result.era = Calendar.CurrentEra;
+
+ //
+ // The string to be parsed. Use a __DTString wrapper so that we can trace the index which
+ // indicates the begining of next token.
+ //
+ __DTString str = new __DTString(s, dtfi);
+
+ str.GetNext();
+
+ //
+ // The following loop will break out when we reach the end of the str.
+ //
+ do
+ {
+ //
+ // Call the lexer to get the next token.
+ //
+ // If we find a era in Lex(), the era value will be in raw.era.
+ if (!Lex(dps, ref str, ref dtok, ref raw, ref result, ref dtfi, styles))
+ {
+ TPTraceExit("0000", dps);
+ return false;
+ }
+
+ //
+ // If the token is not unknown, process it.
+ // Otherwise, just discard it.
+ //
+ if (dtok.dtt != DTT.Unk)
+ {
+ //
+ // Check if we got any CJK Date/Time suffix.
+ // Since the Date/Time suffix tells us the number belongs to year/month/day/hour/minute/second,
+ // store the number in the appropriate field in the result.
+ //
+ if (dtok.suffix != TokenType.SEP_Unk)
+ {
+ if (!ProcessDateTimeSuffix(ref result, ref raw, ref dtok))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ TPTraceExit("0010", dps);
+ return false;
+ }
+
+ dtok.suffix = TokenType.SEP_Unk; // Reset suffix to SEP_Unk;
+ }
+
+ if (dtok.dtt == DTT.NumLocalTimeMark)
+ {
+ if (dps == DS.D_YNd || dps == DS.D_YN)
+ {
+ // Consider this as ISO 8601 format:
+ // "yyyy-MM-dd'T'HH:mm:ss" 1999-10-31T02:00:00
+ TPTraceExit("0020", dps);
+ return (ParseISO8601(ref raw, ref str, styles, ref result));
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ TPTraceExit("0030", dps);
+ return false;
+ }
+ }
+
+ if (raw.hasSameDateAndTimeSeparators)
+ {
+ if (dtok.dtt == DTT.YearEnd || dtok.dtt == DTT.YearSpace || dtok.dtt == DTT.YearDateSep)
+ {
+ // When time and date separators are same and we are hitting a year number while the first parsed part of the string was recognized
+ // as part of time (and not a date) DS.T_Nt, DS.T_NNt then change the state to be a date so we try to parse it as a date instead
+ if (dps == DS.T_Nt)
+ {
+ dps = DS.D_Nd;
+ }
+ if (dps == DS.T_NNt)
+ {
+ dps = DS.D_NNd;
+ }
+ }
+
+ bool atEnd = str.AtEnd();
+ if (dateParsingStates[(int)dps][(int)dtok.dtt] == DS.ERROR || atEnd)
+ {
+ switch (dtok.dtt)
+ {
+ // we have the case of Serbia have dates in forms 'd.M.yyyy.' so we can expect '.' after the date parts.
+ // changing the token to end with space instead of Date Separator will avoid failing the parsing.
+
+ case DTT.YearDateSep: dtok.dtt = atEnd ? DTT.YearEnd : DTT.YearSpace; break;
+ case DTT.NumDatesep: dtok.dtt = atEnd ? DTT.NumEnd : DTT.NumSpace; break;
+ case DTT.NumTimesep: dtok.dtt = atEnd ? DTT.NumEnd : DTT.NumSpace; break;
+ case DTT.MonthDatesep: dtok.dtt = atEnd ? DTT.MonthEnd : DTT.MonthSpace; break;
+ }
+ }
+ }
+
+ //
+ // Advance to the next state, and continue
+ //
+ dps = dateParsingStates[(int)dps][(int)dtok.dtt];
+
+ if (dps == DS.ERROR)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ TPTraceExit("0040 (invalid state transition)", dps);
+ return false;
+ }
+ else if (dps > DS.ERROR)
+ {
+ if ((dtfi.FormatFlags & DateTimeFormatFlags.UseHebrewRule) != 0)
+ {
+ if (!ProcessHebrewTerminalState(dps, ref result, ref styles, ref raw, dtfi))
+ {
+ TPTraceExit("0050 (ProcessHebrewTerminalState)", dps);
+ return false;
+ }
+ }
+ else
+ {
+ if (!ProcessTerminaltState(dps, ref result, ref styles, ref raw, dtfi))
+ {
+ TPTraceExit("0060 (ProcessTerminaltState)", dps);
+ return false;
+ }
+ }
+ reachTerminalState = true;
+
+ //
+ // If we have reached a terminal state, start over from DS.BEGIN again.
+ // For example, when we parsed "1999-12-23 13:30", we will reach a terminal state at "1999-12-23",
+ // and we start over so we can continue to parse "12:30".
+ //
+ dps = DS.BEGIN;
+ }
+ }
+ } while (dtok.dtt != DTT.End && dtok.dtt != DTT.NumEnd && dtok.dtt != DTT.MonthEnd);
+
+ if (!reachTerminalState)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ TPTraceExit("0070 (did not reach terminal state)", dps);
+ return false;
+ }
+
+ AdjustTimeMark(dtfi, ref raw);
+ if (!AdjustHour(ref result.Hour, raw.timeMark))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ TPTraceExit("0080 (AdjustHour)", dps);
+ return false;
+ }
+
+ // Check if the parased string only contains hour/minute/second values.
+ bool bTimeOnly = (result.Year == -1 && result.Month == -1 && result.Day == -1);
+
+ //
+ // Check if any year/month/day is missing in the parsing string.
+ // If yes, get the default value from today's date.
+ //
+ if (!CheckDefaultDateTime(ref result, ref result.calendar, styles))
+ {
+ TPTraceExit("0090 (failed to fill in missing year/month/day defaults)", dps);
+ return false;
+ }
+
+ if (!result.calendar.TryToDateTime(result.Year, result.Month, result.Day,
+ result.Hour, result.Minute, result.Second, 0, result.era, out time))
+ {
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ TPTraceExit("0100 (result.calendar.TryToDateTime)", dps);
+ return false;
+ }
+ if (raw.fraction > 0)
+ {
+ time = time.AddTicks((long)Math.Round(raw.fraction * Calendar.TicksPerSecond));
+ }
+
+ //
+ // We have to check day of week before we adjust to the time zone.
+ // Otherwise, the value of day of week may change after adjustting to the time zone.
+ //
+ if (raw.dayOfWeek != -1)
+ {
+ //
+ // Check if day of week is correct.
+ //
+ if (raw.dayOfWeek != (int)result.calendar.GetDayOfWeek(time))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null);
+ TPTraceExit("0110 (dayOfWeek check)", dps);
+ return false;
+ }
+ }
+
+ result.parsedDate = time;
+
+ if (!DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly))
+ {
+ TPTraceExit("0120 (DetermineTimeZoneAdjustments)", dps);
+ return false;
+ }
+ TPTraceExit("0130 (success)", dps);
+ return true;
+ }
+
+
+ // Handles time zone adjustments and sets DateTimeKind values as required by the styles
+ private static Boolean DetermineTimeZoneAdjustments(ref DateTimeResult result, DateTimeStyles styles, Boolean bTimeOnly)
+ {
+ if ((result.flags & ParseFlags.CaptureOffset) != 0)
+ {
+ // This is a DateTimeOffset parse, so the offset will actually be captured directly, and
+ // no adjustment is required in most cases
+ return DateTimeOffsetTimeZonePostProcessing(ref result, styles);
+ }
+ else
+ {
+ Int64 offsetTicks = result.timeZoneOffset.Ticks;
+
+ // the DateTime offset must be within +- 14:00 hours.
+ if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null);
+ return false;
+ }
+ }
+
+ // The flags AssumeUniveral and AssumeLocal only apply when the input does not have a time zone
+ if ((result.flags & ParseFlags.TimeZoneUsed) == 0)
+ {
+ // If AssumeLocal or AssumeLocal is used, there will always be a kind specified. As in the
+ // case when a time zone is present, it will default to being local unless AdjustToUniversal
+ // is present. These comparisons determine whether setting the kind is sufficient, or if a
+ // time zone adjustment is required. For consistentcy with the rest of parsing, it is desirable
+ // to fall through to the Adjust methods below, so that there is consist handling of boundary
+ // cases like wrapping around on time-only dates and temporarily allowing an adjusted date
+ // to exceed DateTime.MaxValue
+ if ((styles & DateTimeStyles.AssumeLocal) != 0)
+ {
+ if ((styles & DateTimeStyles.AdjustToUniversal) != 0)
+ {
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = TimeZoneInfo.GetLocalUtcOffset(result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+ }
+ else
+ {
+ result.parsedDate = DateTime.SpecifyKind(result.parsedDate, DateTimeKind.Local);
+ return true;
+ }
+ }
+ else if ((styles & DateTimeStyles.AssumeUniversal) != 0)
+ {
+ if ((styles & DateTimeStyles.AdjustToUniversal) != 0)
+ {
+ result.parsedDate = DateTime.SpecifyKind(result.parsedDate, DateTimeKind.Utc);
+ return true;
+ }
+ else
+ {
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = TimeSpan.Zero;
+ }
+ }
+ else
+ {
+ // No time zone and no Assume flags, so DateTimeKind.Unspecified is fine
+ Debug.Assert(result.parsedDate.Kind == DateTimeKind.Unspecified, "result.parsedDate.Kind == DateTimeKind.Unspecified");
+ return true;
+ }
+ }
+
+ if (((styles & DateTimeStyles.RoundtripKind) != 0) && ((result.flags & ParseFlags.TimeZoneUtc) != 0))
+ {
+ result.parsedDate = DateTime.SpecifyKind(result.parsedDate, DateTimeKind.Utc);
+ return true;
+ }
+
+ if ((styles & DateTimeStyles.AdjustToUniversal) != 0)
+ {
+ return (AdjustTimeZoneToUniversal(ref result));
+ }
+ return (AdjustTimeZoneToLocal(ref result, bTimeOnly));
+ }
+
+ // Apply validation and adjustments specific to DateTimeOffset
+ private static Boolean DateTimeOffsetTimeZonePostProcessing(ref DateTimeResult result, DateTimeStyles styles)
+ {
+ // For DateTimeOffset, default to the Utc or Local offset when an offset was not specified by
+ // the input string.
+ if ((result.flags & ParseFlags.TimeZoneUsed) == 0)
+ {
+ if ((styles & DateTimeStyles.AssumeUniversal) != 0)
+ {
+ // AssumeUniversal causes the offset to default to zero (0)
+ result.timeZoneOffset = TimeSpan.Zero;
+ }
+ else
+ {
+ // AssumeLocal causes the offset to default to Local. This flag is on by default for DateTimeOffset.
+ result.timeZoneOffset = TimeZoneInfo.GetLocalUtcOffset(result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime);
+ }
+ }
+
+ Int64 offsetTicks = result.timeZoneOffset.Ticks;
+
+ // there should be no overflow, because the offset can be no more than -+100 hours and the date already
+ // fits within a DateTime.
+ Int64 utcTicks = result.parsedDate.Ticks - offsetTicks;
+
+ // For DateTimeOffset, both the parsed time and the corresponding UTC value must be within the boundaries
+ // of a DateTime instance.
+ if (utcTicks < DateTime.MinTicks || utcTicks > DateTime.MaxTicks)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_UTCOutOfRange", null);
+ return false;
+ }
+
+ // the offset must be within +- 14:00 hours.
+ if (offsetTicks < DateTimeOffset.MinOffset || offsetTicks > DateTimeOffset.MaxOffset)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_OffsetOutOfRange", null);
+ return false;
+ }
+
+ // DateTimeOffset should still honor the AdjustToUniversal flag for consistency with DateTime. It means you
+ // want to return an adjusted UTC value, so store the utcTicks in the DateTime and set the offset to zero
+ if ((styles & DateTimeStyles.AdjustToUniversal) != 0)
+ {
+ if (((result.flags & ParseFlags.TimeZoneUsed) == 0) && ((styles & DateTimeStyles.AssumeUniversal) == 0))
+ {
+ // Handle the special case where the timeZoneOffset was defaulted to Local
+ Boolean toUtcResult = AdjustTimeZoneToUniversal(ref result);
+ result.timeZoneOffset = TimeSpan.Zero;
+ return toUtcResult;
+ }
+
+ // The constructor should always succeed because of the range check earlier in the function
+ // Althought it is UTC, internally DateTimeOffset does not use this flag
+ result.parsedDate = new DateTime(utcTicks, DateTimeKind.Utc);
+ result.timeZoneOffset = TimeSpan.Zero;
+ }
+
+ return true;
+ }
+
+
+ //
+ // Adjust the specified time to universal time based on the supplied timezone.
+ // E.g. when parsing "2001/06/08 14:00-07:00",
+ // the time is 2001/06/08 14:00, and timeZoneOffset = -07:00.
+ // The result will be "2001/06/08 21:00"
+ //
+ private static Boolean AdjustTimeZoneToUniversal(ref DateTimeResult result)
+ {
+ long resultTicks = result.parsedDate.Ticks;
+ resultTicks -= result.timeZoneOffset.Ticks;
+ if (resultTicks < 0)
+ {
+ resultTicks += Calendar.TicksPerDay;
+ }
+
+ if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null);
+ return false;
+ }
+ result.parsedDate = new DateTime(resultTicks, DateTimeKind.Utc);
+ return true;
+ }
+
+ //
+ // Adjust the specified time to universal time based on the supplied timezone,
+ // and then convert to local time.
+ // E.g. when parsing "2001/06/08 14:00-04:00", and local timezone is GMT-7.
+ // the time is 2001/06/08 14:00, and timeZoneOffset = -05:00.
+ // The result will be "2001/06/08 11:00"
+ //
+ private static Boolean AdjustTimeZoneToLocal(ref DateTimeResult result, bool bTimeOnly)
+ {
+ long resultTicks = result.parsedDate.Ticks;
+ // Convert to local ticks
+ TimeZoneInfo tz = TimeZoneInfo.Local;
+ Boolean isAmbiguousLocalDst = false;
+ if (resultTicks < Calendar.TicksPerDay)
+ {
+ //
+ // This is time of day.
+ //
+
+ // Adjust timezone.
+ resultTicks -= result.timeZoneOffset.Ticks;
+ // If the time is time of day, use the current timezone offset.
+ resultTicks += tz.GetUtcOffset(bTimeOnly ? DateTime.Now : result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks;
+
+ if (resultTicks < 0)
+ {
+ resultTicks += Calendar.TicksPerDay;
+ }
+ }
+ else
+ {
+ // Adjust timezone to GMT.
+ resultTicks -= result.timeZoneOffset.Ticks;
+ if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks)
+ {
+ // If the result ticks is greater than DateTime.MaxValue, we can not create a DateTime from this ticks.
+ // In this case, keep using the old code.
+ resultTicks += tz.GetUtcOffset(result.parsedDate, TimeZoneInfoOptions.NoThrowOnInvalidTime).Ticks;
+ }
+ else
+ {
+ // Convert the GMT time to local time.
+ DateTime utcDt = new DateTime(resultTicks, DateTimeKind.Utc);
+ Boolean isDaylightSavings = false;
+ resultTicks += TimeZoneInfo.GetUtcOffsetFromUtc(utcDt, TimeZoneInfo.Local, out isDaylightSavings, out isAmbiguousLocalDst).Ticks;
+ }
+ }
+ if (resultTicks < DateTime.MinTicks || resultTicks > DateTime.MaxTicks)
+ {
+ result.parsedDate = DateTime.MinValue;
+ result.SetFailure(ParseFailureKind.Format, "Format_DateOutOfRange", null);
+ return false;
+ }
+ result.parsedDate = new DateTime(resultTicks, DateTimeKind.Local, isAmbiguousLocalDst);
+ return true;
+ }
+
+ //
+ // Parse the ISO8601 format string found during Parse();
+ //
+ //
+ private static bool ParseISO8601(ref DateTimeRawInfo raw, ref __DTString str, DateTimeStyles styles, ref DateTimeResult result)
+ {
+ if (raw.year < 0 || raw.GetNumber(0) < 0 || raw.GetNumber(1) < 0)
+ {
+ }
+ str.Index--;
+ int hour, minute;
+ int second = 0;
+ double partSecond = 0;
+
+ str.SkipWhiteSpaces();
+ if (!ParseDigits(ref str, 2, out hour))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ str.SkipWhiteSpaces();
+ if (!str.Match(':'))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ str.SkipWhiteSpaces();
+ if (!ParseDigits(ref str, 2, out minute))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ str.SkipWhiteSpaces();
+ if (str.Match(':'))
+ {
+ str.SkipWhiteSpaces();
+ if (!ParseDigits(ref str, 2, out second))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ if (str.Match('.'))
+ {
+ if (!ParseFraction(ref str, out partSecond))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ str.Index--;
+ }
+ str.SkipWhiteSpaces();
+ }
+ if (str.GetNext())
+ {
+ char ch = str.GetChar();
+ if (ch == '+' || ch == '-')
+ {
+ result.flags |= ParseFlags.TimeZoneUsed;
+ if (!ParseTimeZone(ref str, ref result.timeZoneOffset))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ }
+ else if (ch == 'Z' || ch == 'z')
+ {
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = TimeSpan.Zero;
+ result.flags |= ParseFlags.TimeZoneUtc;
+ }
+ else
+ {
+ str.Index--;
+ }
+ str.SkipWhiteSpaces();
+ if (str.Match('#'))
+ {
+ if (!VerifyValidPunctuation(ref str))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ str.SkipWhiteSpaces();
+ }
+ if (str.Match('\0'))
+ {
+ if (!VerifyValidPunctuation(ref str))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ }
+ if (str.GetNext())
+ {
+ // If this is true, there were non-white space characters remaining in the DateTime
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ }
+
+ DateTime time;
+ Calendar calendar = GregorianCalendar.GetDefaultInstance();
+ if (!calendar.TryToDateTime(raw.year, raw.GetNumber(0), raw.GetNumber(1),
+ hour, minute, second, 0, result.era, out time))
+ {
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ return false;
+ }
+
+ time = time.AddTicks((long)Math.Round(partSecond * Calendar.TicksPerSecond));
+ result.parsedDate = time;
+ if (!DetermineTimeZoneAdjustments(ref result, styles, false))
+ {
+ return false;
+ }
+ return true;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Actions:
+ // Parse the current word as a Hebrew number.
+ // This is used by DateTime.ParseExact().
+ //
+ ////////////////////////////////////////////////////////////////////////
+
+ internal static bool MatchHebrewDigits(ref __DTString str, int digitLen, out int number)
+ {
+ number = 0;
+
+ // Create a context object so that we can parse the Hebrew number text character by character.
+ HebrewNumberParsingContext context = new HebrewNumberParsingContext(0);
+
+ // Set this to ContinueParsing so that we will run the following while loop in the first time.
+ HebrewNumberParsingState state = HebrewNumberParsingState.ContinueParsing;
+
+ while (state == HebrewNumberParsingState.ContinueParsing && str.GetNext())
+ {
+ state = HebrewNumber.ParseByChar(str.GetChar(), ref context);
+ }
+
+ if (state == HebrewNumberParsingState.FoundEndOfHebrewNumber)
+ {
+ // If we have reached a terminal state, update the result and returns.
+ number = context.result;
+ return (true);
+ }
+
+ // If we run out of the character before reaching FoundEndOfHebrewNumber, or
+ // the state is InvalidHebrewNumber or ContinueParsing, we fail to match a Hebrew number.
+ // Return an error.
+ return false;
+ }
+
+ /*=================================ParseDigits==================================
+ **Action: Parse the number string in __DTString that are formatted using
+ ** the following patterns:
+ ** "0", "00", and "000..0"
+ **Returns: the integer value
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if error in parsing number.
+ ==============================================================================*/
+
+ internal static bool ParseDigits(ref __DTString str, int digitLen, out int result)
+ {
+ if (digitLen == 1)
+ {
+ // 1 really means 1 or 2 for this call
+ return ParseDigits(ref str, 1, 2, out result);
+ }
+ else
+ {
+ return ParseDigits(ref str, digitLen, digitLen, out result);
+ }
+ }
+
+ internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDigitLen, out int result)
+ {
+ Debug.Assert(minDigitLen > 0, "minDigitLen > 0");
+ Debug.Assert(maxDigitLen < 9, "maxDigitLen < 9");
+ Debug.Assert(minDigitLen <= maxDigitLen, "minDigitLen <= maxDigitLen");
+ result = 0;
+ int startingIndex = str.Index;
+ int tokenLength = 0;
+ while (tokenLength < maxDigitLen)
+ {
+ if (!str.GetNextDigit())
+ {
+ str.Index--;
+ break;
+ }
+ result = result * 10 + str.GetDigit();
+ tokenLength++;
+ }
+ if (tokenLength < minDigitLen)
+ {
+ str.Index = startingIndex;
+ return false;
+ }
+ return true;
+ }
+
+ /*=================================ParseFractionExact==================================
+ **Action: Parse the number string in __DTString that are formatted using
+ ** the following patterns:
+ ** "0", "00", and "000..0"
+ **Returns: the fraction value
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if error in parsing number.
+ ==============================================================================*/
+
+ private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, ref double result)
+ {
+ if (!str.GetNextDigit())
+ {
+ str.Index--;
+ return false;
+ }
+ result = str.GetDigit();
+
+ int digitLen = 1;
+ for (; digitLen < maxDigitLen; digitLen++)
+ {
+ if (!str.GetNextDigit())
+ {
+ str.Index--;
+ break;
+ }
+ result = result * 10 + str.GetDigit();
+ }
+
+ result = ((double)result / Math.Pow(10, digitLen));
+ return (digitLen == maxDigitLen);
+ }
+
+ /*=================================ParseSign==================================
+ **Action: Parse a positive or a negative sign.
+ **Returns: true if postive sign. flase if negative sign.
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if end of string is encountered or a sign
+ ** symbol is not found.
+ ==============================================================================*/
+
+ private static bool ParseSign(ref __DTString str, ref bool result)
+ {
+ if (!str.GetNext())
+ {
+ // A sign symbol ('+' or '-') is expected. However, end of string is encountered.
+ return false;
+ }
+ char ch = str.GetChar();
+ if (ch == '+')
+ {
+ result = true;
+ return (true);
+ }
+ else if (ch == '-')
+ {
+ result = false;
+ return (true);
+ }
+ // A sign symbol ('+' or '-') is expected.
+ return false;
+ }
+
+ /*=================================ParseTimeZoneOffset==================================
+ **Action: Parse the string formatted using "z", "zz", "zzz" in DateTime.Format().
+ **Returns: the TimeSpan for the parsed timezone offset.
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ ** len: the repeated number of the "z"
+ **Exceptions: FormatException if errors in parsing.
+ ==============================================================================*/
+
+ private static bool ParseTimeZoneOffset(ref __DTString str, int len, ref TimeSpan result)
+ {
+ bool isPositive = true;
+ int hourOffset;
+ int minuteOffset = 0;
+
+ switch (len)
+ {
+ case 1:
+ case 2:
+ if (!ParseSign(ref str, ref isPositive))
+ {
+ return (false);
+ }
+ if (!ParseDigits(ref str, len, out hourOffset))
+ {
+ return (false);
+ }
+ break;
+ default:
+ if (!ParseSign(ref str, ref isPositive))
+ {
+ return (false);
+ }
+
+ // Parsing 1 digit will actually parse 1 or 2.
+ if (!ParseDigits(ref str, 1, out hourOffset))
+ {
+ return (false);
+ }
+ // ':' is optional.
+ if (str.Match(":"))
+ {
+ // Found ':'
+ if (!ParseDigits(ref str, 2, out minuteOffset))
+ {
+ return (false);
+ }
+ }
+ else
+ {
+ // Since we can not match ':', put the char back.
+ str.Index--;
+ if (!ParseDigits(ref str, 2, out minuteOffset))
+ {
+ return (false);
+ }
+ }
+ break;
+ }
+ if (minuteOffset < 0 || minuteOffset >= 60)
+ {
+ return false;
+ }
+
+ result = (new TimeSpan(hourOffset, minuteOffset, 0));
+ if (!isPositive)
+ {
+ result = result.Negate();
+ }
+ return (true);
+ }
+
+ /*=================================MatchAbbreviatedMonthName==================================
+ **Action: Parse the abbreviated month name from string starting at str.Index.
+ **Returns: A value from 1 to 12 for the first month to the twelveth month.
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if an abbreviated month name can not be found.
+ ==============================================================================*/
+
+ private static bool MatchAbbreviatedMonthName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result)
+ {
+ int maxMatchStrLen = 0;
+ result = -1;
+ if (str.GetNext())
+ {
+ //
+ // Scan the month names (note that some calendars has 13 months) and find
+ // the matching month name which has the max string length.
+ // We need to do this because some cultures (e.g. "cs-CZ") which have
+ // abbreviated month names with the same prefix.
+ //
+ int monthsInYear = (dtfi.GetMonthName(13).Length == 0 ? 12 : 13);
+ for (int i = 1; i <= monthsInYear; i++)
+ {
+ String searchStr = dtfi.GetAbbreviatedMonthName(i);
+ int matchStrLen = searchStr.Length;
+ if (dtfi.HasSpacesInMonthNames
+ ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen)
+ : str.MatchSpecifiedWord(searchStr))
+ {
+ if (matchStrLen > maxMatchStrLen)
+ {
+ maxMatchStrLen = matchStrLen;
+ result = i;
+ }
+ }
+ }
+
+ // Search leap year form.
+ if ((dtfi.FormatFlags & DateTimeFormatFlags.UseLeapYearMonth) != 0)
+ {
+ int tempResult = str.MatchLongestWords(dtfi.internalGetLeapYearMonthNames(), ref maxMatchStrLen);
+ // We found a longer match in the leap year month name. Use this as the result.
+ // The result from MatchLongestWords is 0 ~ length of word array.
+ // So we increment the result by one to become the month value.
+ if (tempResult >= 0)
+ {
+ result = tempResult + 1;
+ }
+ }
+ }
+ if (result > 0)
+ {
+ str.Index += (maxMatchStrLen - 1);
+ return (true);
+ }
+ return false;
+ }
+
+ /*=================================MatchMonthName==================================
+ **Action: Parse the month name from string starting at str.Index.
+ **Returns: A value from 1 to 12 indicating the first month to the twelveth month.
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if a month name can not be found.
+ ==============================================================================*/
+
+ private static bool MatchMonthName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result)
+ {
+ int maxMatchStrLen = 0;
+ result = -1;
+ if (str.GetNext())
+ {
+ //
+ // Scan the month names (note that some calendars has 13 months) and find
+ // the matching month name which has the max string length.
+ // We need to do this because some cultures (e.g. "vi-VN") which have
+ // month names with the same prefix.
+ //
+ int monthsInYear = (dtfi.GetMonthName(13).Length == 0 ? 12 : 13);
+ for (int i = 1; i <= monthsInYear; i++)
+ {
+ String searchStr = dtfi.GetMonthName(i);
+ int matchStrLen = searchStr.Length;
+ if (dtfi.HasSpacesInMonthNames
+ ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen)
+ : str.MatchSpecifiedWord(searchStr))
+ {
+ if (matchStrLen > maxMatchStrLen)
+ {
+ maxMatchStrLen = matchStrLen;
+ result = i;
+ }
+ }
+ }
+
+ // Search genitive form.
+ if ((dtfi.FormatFlags & DateTimeFormatFlags.UseGenitiveMonth) != 0)
+ {
+ int tempResult = str.MatchLongestWords(dtfi.MonthGenitiveNames, ref maxMatchStrLen);
+ // We found a longer match in the genitive month name. Use this as the result.
+ // The result from MatchLongestWords is 0 ~ length of word array.
+ // So we increment the result by one to become the month value.
+ if (tempResult >= 0)
+ {
+ result = tempResult + 1;
+ }
+ }
+
+ // Search leap year form.
+ if ((dtfi.FormatFlags & DateTimeFormatFlags.UseLeapYearMonth) != 0)
+ {
+ int tempResult = str.MatchLongestWords(dtfi.internalGetLeapYearMonthNames(), ref maxMatchStrLen);
+ // We found a longer match in the leap year month name. Use this as the result.
+ // The result from MatchLongestWords is 0 ~ length of word array.
+ // So we increment the result by one to become the month value.
+ if (tempResult >= 0)
+ {
+ result = tempResult + 1;
+ }
+ }
+ }
+
+ if (result > 0)
+ {
+ str.Index += (maxMatchStrLen - 1);
+ return (true);
+ }
+ return false;
+ }
+
+ /*=================================MatchAbbreviatedDayName==================================
+ **Action: Parse the abbreviated day of week name from string starting at str.Index.
+ **Returns: A value from 0 to 6 indicating Sunday to Saturday.
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if a abbreviated day of week name can not be found.
+ ==============================================================================*/
+
+ private static bool MatchAbbreviatedDayName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result)
+ {
+ int maxMatchStrLen = 0;
+ result = -1;
+ if (str.GetNext())
+ {
+ for (DayOfWeek i = DayOfWeek.Sunday; i <= DayOfWeek.Saturday; i++)
+ {
+ String searchStr = dtfi.GetAbbreviatedDayName(i);
+ int matchStrLen = searchStr.Length;
+ if (dtfi.HasSpacesInDayNames
+ ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen)
+ : str.MatchSpecifiedWord(searchStr))
+ {
+ if (matchStrLen > maxMatchStrLen)
+ {
+ maxMatchStrLen = matchStrLen;
+ result = (int)i;
+ }
+ }
+ }
+ }
+ if (result >= 0)
+ {
+ str.Index += maxMatchStrLen - 1;
+ return (true);
+ }
+ return false;
+ }
+
+ /*=================================MatchDayName==================================
+ **Action: Parse the day of week name from string starting at str.Index.
+ **Returns: A value from 0 to 6 indicating Sunday to Saturday.
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if a day of week name can not be found.
+ ==============================================================================*/
+
+ private static bool MatchDayName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result)
+ {
+ // Turkish (tr-TR) got day names with the same prefix.
+ int maxMatchStrLen = 0;
+ result = -1;
+ if (str.GetNext())
+ {
+ for (DayOfWeek i = DayOfWeek.Sunday; i <= DayOfWeek.Saturday; i++)
+ {
+ String searchStr = dtfi.GetDayName(i);
+ int matchStrLen = searchStr.Length;
+ if (dtfi.HasSpacesInDayNames
+ ? str.MatchSpecifiedWords(searchStr, false, ref matchStrLen)
+ : str.MatchSpecifiedWord(searchStr))
+ {
+ if (matchStrLen > maxMatchStrLen)
+ {
+ maxMatchStrLen = matchStrLen;
+ result = (int)i;
+ }
+ }
+ }
+ }
+ if (result >= 0)
+ {
+ str.Index += maxMatchStrLen - 1;
+ return (true);
+ }
+ return false;
+ }
+
+ /*=================================MatchEraName==================================
+ **Action: Parse era name from string starting at str.Index.
+ **Returns: An era value.
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if an era name can not be found.
+ ==============================================================================*/
+
+ private static bool MatchEraName(ref __DTString str, DateTimeFormatInfo dtfi, ref int result)
+ {
+ if (str.GetNext())
+ {
+ int[] eras = dtfi.Calendar.Eras;
+
+ if (eras != null)
+ {
+ for (int i = 0; i < eras.Length; i++)
+ {
+ String searchStr = dtfi.GetEraName(eras[i]);
+ if (str.MatchSpecifiedWord(searchStr))
+ {
+ str.Index += (searchStr.Length - 1);
+ result = eras[i];
+ return (true);
+ }
+ searchStr = dtfi.GetAbbreviatedEraName(eras[i]);
+ if (str.MatchSpecifiedWord(searchStr))
+ {
+ str.Index += (searchStr.Length - 1);
+ result = eras[i];
+ return (true);
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /*=================================MatchTimeMark==================================
+ **Action: Parse the time mark (AM/PM) from string starting at str.Index.
+ **Returns: TM_AM or TM_PM.
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if a time mark can not be found.
+ ==============================================================================*/
+
+ private static bool MatchTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, ref TM result)
+ {
+ result = TM.NotSet;
+ // In some cultures have empty strings in AM/PM mark. E.g. af-ZA (0x0436), the AM mark is "", and PM mark is "nm".
+ if (dtfi.AMDesignator.Length == 0)
+ {
+ result = TM.AM;
+ }
+ if (dtfi.PMDesignator.Length == 0)
+ {
+ result = TM.PM;
+ }
+
+ if (str.GetNext())
+ {
+ String searchStr = dtfi.AMDesignator;
+ if (searchStr.Length > 0)
+ {
+ if (str.MatchSpecifiedWord(searchStr))
+ {
+ // Found an AM timemark with length > 0.
+ str.Index += (searchStr.Length - 1);
+ result = TM.AM;
+ return (true);
+ }
+ }
+ searchStr = dtfi.PMDesignator;
+ if (searchStr.Length > 0)
+ {
+ if (str.MatchSpecifiedWord(searchStr))
+ {
+ // Found a PM timemark with length > 0.
+ str.Index += (searchStr.Length - 1);
+ result = TM.PM;
+ return (true);
+ }
+ }
+ str.Index--; // Undo the GetNext call.
+ }
+ if (result != TM.NotSet)
+ {
+ // If one of the AM/PM marks is empty string, return the result.
+ return (true);
+ }
+ return false;
+ }
+
+ /*=================================MatchAbbreviatedTimeMark==================================
+ **Action: Parse the abbreviated time mark (AM/PM) from string starting at str.Index.
+ **Returns: TM_AM or TM_PM.
+ **Arguments: str: a __DTString. The parsing will start from the
+ ** next character after str.Index.
+ **Exceptions: FormatException if a abbreviated time mark can not be found.
+ ==============================================================================*/
+
+ private static bool MatchAbbreviatedTimeMark(ref __DTString str, DateTimeFormatInfo dtfi, ref TM result)
+ {
+ // NOTENOTE : the assumption here is that abbreviated time mark is the first
+ // character of the AM/PM designator. If this invariant changes, we have to
+ // change the code below.
+ if (str.GetNext())
+ {
+ if (str.GetChar() == dtfi.AMDesignator[0])
+ {
+ result = TM.AM;
+ return (true);
+ }
+ if (str.GetChar() == dtfi.PMDesignator[0])
+ {
+ result = TM.PM;
+ return (true);
+ }
+ }
+ return false;
+ }
+
+ /*=================================CheckNewValue==================================
+ **Action: Check if currentValue is initialized. If not, return the newValue.
+ ** If yes, check if the current value is equal to newValue. Return false
+ ** if they are not equal. This is used to check the case like "d" and "dd" are both
+ ** used to format a string.
+ **Returns: the correct value for currentValue.
+ **Arguments:
+ **Exceptions:
+ ==============================================================================*/
+
+ private static bool CheckNewValue(ref int currentValue, int newValue, char patternChar, ref DateTimeResult result)
+ {
+ if (currentValue == -1)
+ {
+ currentValue = newValue;
+ return (true);
+ }
+ else
+ {
+ if (newValue != currentValue)
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", patternChar);
+ return (false);
+ }
+ }
+ return (true);
+ }
+
+ private static DateTime GetDateTimeNow(ref DateTimeResult result, ref DateTimeStyles styles)
+ {
+ if ((result.flags & ParseFlags.CaptureOffset) != 0)
+ {
+ if ((result.flags & ParseFlags.TimeZoneUsed) != 0)
+ {
+ // use the supplied offset to calculate 'Now'
+ return new DateTime(DateTime.UtcNow.Ticks + result.timeZoneOffset.Ticks, DateTimeKind.Unspecified);
+ }
+ else if ((styles & DateTimeStyles.AssumeUniversal) != 0)
+ {
+ // assume the offset is Utc
+ return DateTime.UtcNow;
+ }
+ }
+
+ // assume the offset is Local
+ return DateTime.Now;
+ }
+
+ private static bool CheckDefaultDateTime(ref DateTimeResult result, ref Calendar cal, DateTimeStyles styles)
+ {
+ if ((result.flags & ParseFlags.CaptureOffset) != 0)
+ {
+ // DateTimeOffset.Parse should allow dates without a year, but only if there is also no time zone marker;
+ // e.g. "May 1 5pm" is OK, but "May 1 5pm -08:30" is not. This is somewhat pragmatic, since we would
+ // have to rearchitect parsing completely to allow this one case to correctly handle things like leap
+ // years and leap months. Is is an extremely corner case, and DateTime is basically incorrect in that
+ // case today.
+ //
+ // values like "11:00Z" or "11:00 -3:00" are also acceptable
+ //
+ // if ((month or day is set) and (year is not set and time zone is set))
+ //
+ if (((result.Month != -1) || (result.Day != -1))
+ && ((result.Year == -1 || ((result.flags & ParseFlags.YearDefault) != 0)) && (result.flags & ParseFlags.TimeZoneUsed) != 0))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_MissingIncompleteDate", null);
+ return false;
+ }
+ }
+
+
+ if ((result.Year == -1) || (result.Month == -1) || (result.Day == -1))
+ {
+ /*
+ The following table describes the behaviors of getting the default value
+ when a certain year/month/day values are missing.
+
+ An "X" means that the value exists. And "--" means that value is missing.
+
+ Year Month Day => ResultYear ResultMonth ResultDay Note
+
+ X X X Parsed year Parsed month Parsed day
+ X X -- Parsed Year Parsed month First day If we have year and month, assume the first day of that month.
+ X -- X Parsed year First month Parsed day If the month is missing, assume first month of that year.
+ X -- -- Parsed year First month First day If we have only the year, assume the first day of that year.
+
+ -- X X CurrentYear Parsed month Parsed day If the year is missing, assume the current year.
+ -- X -- CurrentYear Parsed month First day If we have only a month value, assume the current year and current day.
+ -- -- X CurrentYear First month Parsed day If we have only a day value, assume current year and first month.
+ -- -- -- CurrentYear Current month Current day So this means that if the date string only contains time, you will get current date.
+
+ */
+
+ DateTime now = GetDateTimeNow(ref result, ref styles);
+ if (result.Month == -1 && result.Day == -1)
+ {
+ if (result.Year == -1)
+ {
+ if ((styles & DateTimeStyles.NoCurrentDateDefault) != 0)
+ {
+ // If there is no year/month/day values, and NoCurrentDateDefault flag is used,
+ // set the year/month/day value to the beginning year/month/day of DateTime().
+ // Note we should be using Gregorian for the year/month/day.
+ cal = GregorianCalendar.GetDefaultInstance();
+ result.Year = result.Month = result.Day = 1;
+ }
+ else
+ {
+ // Year/Month/Day are all missing.
+ result.Year = cal.GetYear(now);
+ result.Month = cal.GetMonth(now);
+ result.Day = cal.GetDayOfMonth(now);
+ }
+ }
+ else
+ {
+ // Month/Day are both missing.
+ result.Month = 1;
+ result.Day = 1;
+ }
+ }
+ else
+ {
+ if (result.Year == -1)
+ {
+ result.Year = cal.GetYear(now);
+ }
+ if (result.Month == -1)
+ {
+ result.Month = 1;
+ }
+ if (result.Day == -1)
+ {
+ result.Day = 1;
+ }
+ }
+ }
+ // Set Hour/Minute/Second to zero if these value are not in str.
+ if (result.Hour == -1) result.Hour = 0;
+ if (result.Minute == -1) result.Minute = 0;
+ if (result.Second == -1) result.Second = 0;
+ if (result.era == -1) result.era = Calendar.CurrentEra;
+ return true;
+ }
+
+ // Expand a pre-defined format string (like "D" for long date) to the real format that
+ // we are going to use in the date time parsing.
+ // This method also set the dtfi according/parseInfo to some special pre-defined
+ // formats.
+ //
+ private static String ExpandPredefinedFormat(String format, ref DateTimeFormatInfo dtfi, ref ParsingInfo parseInfo, ref DateTimeResult result)
+ {
+ //
+ // Check the format to see if we need to override the dtfi to be InvariantInfo,
+ // and see if we need to set up the userUniversalTime flag.
+ //
+ switch (format[0])
+ {
+ case 'o':
+ case 'O': // Round Trip Format
+ parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
+ dtfi = DateTimeFormatInfo.InvariantInfo;
+ break;
+ case 'r':
+ case 'R': // RFC 1123 Standard. (in Universal time)
+ parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
+ dtfi = DateTimeFormatInfo.InvariantInfo;
+
+ if ((result.flags & ParseFlags.CaptureOffset) != 0)
+ {
+ result.flags |= ParseFlags.Rfc1123Pattern;
+ }
+ break;
+ case 's': // Sortable format (in local time)
+ dtfi = DateTimeFormatInfo.InvariantInfo;
+ parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
+ break;
+ case 'u': // Universal time format in sortable format.
+ parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
+ dtfi = DateTimeFormatInfo.InvariantInfo;
+
+ if ((result.flags & ParseFlags.CaptureOffset) != 0)
+ {
+ result.flags |= ParseFlags.UtcSortPattern;
+ }
+ break;
+ case 'U': // Universal time format with culture-dependent format.
+ parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = new TimeSpan(0);
+ result.flags |= ParseFlags.TimeZoneUtc;
+ if (dtfi.Calendar.GetType() != typeof(GregorianCalendar))
+ {
+ dtfi = (DateTimeFormatInfo)dtfi.Clone();
+ dtfi.Calendar = GregorianCalendar.GetDefaultInstance();
+ }
+ break;
+ }
+
+ //
+ // Expand the pre-defined format character to the real format from DateTimeFormatInfo.
+ //
+ return (DateTimeFormat.GetRealFormat(format, dtfi));
+ }
+
+
+
+
+
+ // Given a specified format character, parse and update the parsing result.
+ //
+ private static bool ParseByFormat(
+ ref __DTString str,
+ ref __DTString format,
+ ref ParsingInfo parseInfo,
+ DateTimeFormatInfo dtfi,
+ ref DateTimeResult result)
+ {
+ int tokenLen = 0;
+ int tempYear = 0, tempMonth = 0, tempDay = 0, tempDayOfWeek = 0, tempHour = 0, tempMinute = 0, tempSecond = 0;
+ double tempFraction = 0;
+ TM tempTimeMark = 0;
+
+ char ch = format.GetChar();
+
+ switch (ch)
+ {
+ case 'y':
+ tokenLen = format.GetRepeatCount();
+ bool parseResult;
+ if (dtfi.HasForceTwoDigitYears)
+ {
+ parseResult = ParseDigits(ref str, 1, 4, out tempYear);
+ }
+ else
+ {
+ if (tokenLen <= 2)
+ {
+ parseInfo.fUseTwoDigitYear = true;
+ }
+ parseResult = ParseDigits(ref str, tokenLen, out tempYear);
+ }
+ if (!parseResult && parseInfo.fCustomNumberParser)
+ {
+ parseResult = parseInfo.parseNumberDelegate(ref str, tokenLen, out tempYear);
+ }
+ if (!parseResult)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ if (!CheckNewValue(ref result.Year, tempYear, ch, ref result))
+ {
+ return (false);
+ }
+ break;
+ case 'M':
+ tokenLen = format.GetRepeatCount();
+ if (tokenLen <= 2)
+ {
+ if (!ParseDigits(ref str, tokenLen, out tempMonth))
+ {
+ if (!parseInfo.fCustomNumberParser ||
+ !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempMonth))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ }
+ }
+ else
+ {
+ if (tokenLen == 3)
+ {
+ if (!MatchAbbreviatedMonthName(ref str, dtfi, ref tempMonth))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ }
+ else
+ {
+ if (!MatchMonthName(ref str, dtfi, ref tempMonth))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ }
+ result.flags |= ParseFlags.ParsedMonthName;
+ }
+ if (!CheckNewValue(ref result.Month, tempMonth, ch, ref result))
+ {
+ return (false);
+ }
+ break;
+ case 'd':
+ // Day & Day of week
+ tokenLen = format.GetRepeatCount();
+ if (tokenLen <= 2)
+ {
+ // "d" & "dd"
+
+ if (!ParseDigits(ref str, tokenLen, out tempDay))
+ {
+ if (!parseInfo.fCustomNumberParser ||
+ !parseInfo.parseNumberDelegate(ref str, tokenLen, out tempDay))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ }
+ if (!CheckNewValue(ref result.Day, tempDay, ch, ref result))
+ {
+ return (false);
+ }
+ }
+ else
+ {
+ if (tokenLen == 3)
+ {
+ // "ddd"
+ if (!MatchAbbreviatedDayName(ref str, dtfi, ref tempDayOfWeek))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ }
+ else
+ {
+ // "dddd*"
+ if (!MatchDayName(ref str, dtfi, ref tempDayOfWeek))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ }
+ if (!CheckNewValue(ref parseInfo.dayOfWeek, tempDayOfWeek, ch, ref result))
+ {
+ return (false);
+ }
+ }
+ break;
+ case 'g':
+ tokenLen = format.GetRepeatCount();
+ // Put the era value in result.era.
+ if (!MatchEraName(ref str, dtfi, ref result.era))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ break;
+ case 'h':
+ parseInfo.fUseHour12 = true;
+ tokenLen = format.GetRepeatCount();
+ if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result))
+ {
+ return (false);
+ }
+ break;
+ case 'H':
+ tokenLen = format.GetRepeatCount();
+ if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempHour))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ if (!CheckNewValue(ref result.Hour, tempHour, ch, ref result))
+ {
+ return (false);
+ }
+ break;
+ case 'm':
+ tokenLen = format.GetRepeatCount();
+ if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempMinute))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ if (!CheckNewValue(ref result.Minute, tempMinute, ch, ref result))
+ {
+ return (false);
+ }
+ break;
+ case 's':
+ tokenLen = format.GetRepeatCount();
+ if (!ParseDigits(ref str, (tokenLen < 2 ? 1 : 2), out tempSecond))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ if (!CheckNewValue(ref result.Second, tempSecond, ch, ref result))
+ {
+ return (false);
+ }
+ break;
+ case 'f':
+ case 'F':
+ tokenLen = format.GetRepeatCount();
+ if (tokenLen <= DateTimeFormat.MaxSecondsFractionDigits)
+ {
+ if (!ParseFractionExact(ref str, tokenLen, ref tempFraction))
+ {
+ if (ch == 'f')
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ }
+ if (result.fraction < 0)
+ {
+ result.fraction = tempFraction;
+ }
+ else
+ {
+ if (tempFraction != result.fraction)
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch);
+ return (false);
+ }
+ }
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ break;
+ case 't':
+ // AM/PM designator
+ tokenLen = format.GetRepeatCount();
+ if (tokenLen == 1)
+ {
+ if (!MatchAbbreviatedTimeMark(ref str, dtfi, ref tempTimeMark))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ }
+ else
+ {
+ if (!MatchTimeMark(ref str, dtfi, ref tempTimeMark))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ }
+
+ if (parseInfo.timeMark == TM.NotSet)
+ {
+ parseInfo.timeMark = tempTimeMark;
+ }
+ else
+ {
+ if (parseInfo.timeMark != tempTimeMark)
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", ch);
+ return (false);
+ }
+ }
+ break;
+ case 'z':
+ // timezone offset
+ tokenLen = format.GetRepeatCount();
+ {
+ TimeSpan tempTimeZoneOffset = new TimeSpan(0);
+ if (!ParseTimeZoneOffset(ref str, tokenLen, ref tempTimeZoneOffset))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset)
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'z');
+ return (false);
+ }
+ result.timeZoneOffset = tempTimeZoneOffset;
+ result.flags |= ParseFlags.TimeZoneUsed;
+ }
+ break;
+ case 'Z':
+ if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero)
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'Z');
+ return (false);
+ }
+
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = new TimeSpan(0);
+ result.flags |= ParseFlags.TimeZoneUtc;
+
+ // The updating of the indexes is to reflect that ParseExact MatchXXX methods assume that
+ // they need to increment the index and Parse GetXXX do not. Since we are calling a Parse
+ // method from inside ParseExact we need to adjust this. Long term, we should try to
+ // eliminate this discrepancy.
+ str.Index++;
+ if (!GetTimeZoneName(ref str))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ str.Index--;
+ break;
+ case 'K':
+ // This should parse either as a blank, the 'Z' character or a local offset like "-07:00"
+ if (str.Match('Z'))
+ {
+ if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && result.timeZoneOffset != TimeSpan.Zero)
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K');
+ return (false);
+ }
+
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = new TimeSpan(0);
+ result.flags |= ParseFlags.TimeZoneUtc;
+ }
+ else if (str.Match('+') || str.Match('-'))
+ {
+ str.Index--; // Put the character back for the parser
+ TimeSpan tempTimeZoneOffset = new TimeSpan(0);
+ if (!ParseTimeZoneOffset(ref str, 3, ref tempTimeZoneOffset))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return (false);
+ }
+ if ((result.flags & ParseFlags.TimeZoneUsed) != 0 && tempTimeZoneOffset != result.timeZoneOffset)
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_RepeatDateTimePattern", 'K');
+ return (false);
+ }
+ result.timeZoneOffset = tempTimeZoneOffset;
+ result.flags |= ParseFlags.TimeZoneUsed;
+ }
+ // Otherwise it is unspecified and we consume no characters
+ break;
+ case ':':
+ // We match the separator in time pattern with the character in the time string if both equal to ':' or the date separator is matching the characters in the date string
+ // We have to exclude the case when the time separator is more than one character and starts with ':' something like "::" for instance.
+ if (((dtfi.TimeSeparator.Length > 1 && dtfi.TimeSeparator[0] == ':') || !str.Match(':')) &&
+ !str.Match(dtfi.TimeSeparator))
+ {
+ // A time separator is expected.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ break;
+ case '/':
+ // We match the separator in date pattern with the character in the date string if both equal to '/' or the date separator is matching the characters in the date string
+ // We have to exclude the case when the date separator is more than one character and starts with '/' something like "//" for instance.
+ if (((dtfi.DateSeparator.Length > 1 && dtfi.DateSeparator[0] == '/') || !str.Match('/')) &&
+ !str.Match(dtfi.DateSeparator))
+ {
+ // A date separator is expected.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ break;
+ case '\"':
+ case '\'':
+ StringBuilder enquotedString = new StringBuilder();
+ // Use ParseQuoteString so that we can handle escape characters within the quoted string.
+ if (!TryParseQuoteString(format.Value, format.Index, enquotedString, out tokenLen))
+ {
+ result.SetFailure(ParseFailureKind.FormatWithParameter, "Format_BadQuote", ch);
+ return (false);
+ }
+ format.Index += tokenLen - 1;
+
+ // Some cultures uses space in the quoted string. E.g. Spanish has long date format as:
+ // "dddd, dd' de 'MMMM' de 'yyyy". When inner spaces flag is set, we should skip whitespaces if there is space
+ // in the quoted string.
+ String quotedStr = enquotedString.ToString();
+
+ for (int i = 0; i < quotedStr.Length; i++)
+ {
+ if (quotedStr[i] == ' ' && parseInfo.fAllowInnerWhite)
+ {
+ str.SkipWhiteSpaces();
+ }
+ else if (!str.Match(quotedStr[i]))
+ {
+ // Can not find the matching quoted string.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ }
+
+ // The "r" and "u" formats incorrectly quoted 'GMT' and 'Z', respectively. We cannot
+ // correct this mistake for DateTime.ParseExact for compatibility reasons, but we can
+ // fix it for DateTimeOffset.ParseExact as DateTimeOffset has not been publically released
+ // with this issue.
+ if ((result.flags & ParseFlags.CaptureOffset) != 0)
+ {
+ if ((result.flags & ParseFlags.Rfc1123Pattern) != 0 && quotedStr == GMTName)
+ {
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = TimeSpan.Zero;
+ }
+ else if ((result.flags & ParseFlags.UtcSortPattern) != 0 && quotedStr == ZuluName)
+ {
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = TimeSpan.Zero;
+ }
+ }
+
+ break;
+ case '%':
+ // Skip this so we can get to the next pattern character.
+ // Used in case like "%d", "%y"
+
+ // Make sure the next character is not a '%' again.
+ if (format.Index >= format.Value.Length - 1 || format.Value[format.Index + 1] == '%')
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ return false;
+ }
+ break;
+ case '\\':
+ // Escape character. For example, "\d".
+ // Get the next character in format, and see if we can
+ // find a match in str.
+ if (format.GetNext())
+ {
+ if (!str.Match(format.GetChar()))
+ {
+ // Can not find a match for the escaped character.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ }
+ else
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ return false;
+ }
+ break;
+ case '.':
+ if (!str.Match(ch))
+ {
+ if (format.GetNext())
+ {
+ // If we encounter the pattern ".F", and the dot is not present, it is an optional
+ // second fraction and we can skip this format.
+ if (format.Match('F'))
+ {
+ format.GetRepeatCount();
+ break;
+ }
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ break;
+ default:
+ if (ch == ' ')
+ {
+ if (parseInfo.fAllowInnerWhite)
+ {
+ // Skip whitespaces if AllowInnerWhite.
+ // Do nothing here.
+ }
+ else
+ {
+ if (!str.Match(ch))
+ {
+ // If the space does not match, and trailing space is allowed, we do
+ // one more step to see if the next format character can lead to
+ // successful parsing.
+ // This is used to deal with special case that a empty string can match
+ // a specific pattern.
+ // The example here is af-ZA, which has a time format like "hh:mm:ss tt". However,
+ // its AM symbol is "" (empty string). If fAllowTrailingWhite is used, and time is in
+ // the AM, we will trim the whitespaces at the end, which will lead to a failure
+ // when we are trying to match the space before "tt".
+ if (parseInfo.fAllowTrailingWhite)
+ {
+ if (format.GetNext())
+ {
+ if (ParseByFormat(ref str, ref format, ref parseInfo, dtfi, ref result))
+ {
+ return (true);
+ }
+ }
+ }
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ // Found a macth.
+ }
+ }
+ else
+ {
+ if (format.MatchSpecifiedWord(GMTName))
+ {
+ format.Index += (GMTName.Length - 1);
+ // Found GMT string in format. This means the DateTime string
+ // is in GMT timezone.
+ result.flags |= ParseFlags.TimeZoneUsed;
+ result.timeZoneOffset = TimeSpan.Zero;
+ if (!str.Match(GMTName))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ }
+ else if (!str.Match(ch))
+ {
+ // ch is expected.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ }
+ break;
+ } // switch
+ return (true);
+ }
+
+ //
+ // The pos should point to a quote character. This method will
+ // get the string enclosed by the quote character.
+ //
+ internal static bool TryParseQuoteString(String format, int pos, StringBuilder result, out int returnValue)
+ {
+ //
+ // NOTE : pos will be the index of the quote character in the 'format' string.
+ //
+ returnValue = 0;
+ int formatLen = format.Length;
+ int beginPos = pos;
+ char quoteChar = format[pos++]; // Get the character used to quote the following string.
+
+ bool foundQuote = false;
+ while (pos < formatLen)
+ {
+ char ch = format[pos++];
+ if (ch == quoteChar)
+ {
+ foundQuote = true;
+ break;
+ }
+ else if (ch == '\\')
+ {
+ // The following are used to support escaped character.
+ // Escaped character is also supported in the quoted string.
+ // Therefore, someone can use a format like "'minute:' mm\"" to display:
+ // minute: 45"
+ // because the second double quote is escaped.
+ if (pos < formatLen)
+ {
+ result.Append(format[pos++]);
+ }
+ else
+ {
+ //
+ // This means that '\' is at the end of the formatting string.
+ //
+ return false;
+ }
+ }
+ else
+ {
+ result.Append(ch);
+ }
+ }
+
+ if (!foundQuote)
+ {
+ // Here we can't find the matching quote.
+ return false;
+ }
+
+ //
+ // Return the character count including the begin/end quote characters and enclosed string.
+ //
+ returnValue = (pos - beginPos);
+ return true;
+ }
+
+
+
+
+ /*=================================DoStrictParse==================================
+ **Action: Do DateTime parsing using the format in formatParam.
+ **Returns: The parsed DateTime.
+ **Arguments:
+ **Exceptions:
+ **
+ **Notes:
+ ** When the following general formats are used, InvariantInfo is used in dtfi:
+ ** 'r', 'R', 's'.
+ ** When the following general formats are used, the time is assumed to be in Universal time.
+ **
+ **Limitations:
+ ** Only GregarianCalendar is supported for now.
+ ** Only support GMT timezone.
+ ==============================================================================*/
+
+ private static bool DoStrictParse(
+ String s,
+ String formatParam,
+ DateTimeStyles styles,
+ DateTimeFormatInfo dtfi,
+ ref DateTimeResult result)
+ {
+ ParsingInfo parseInfo = new ParsingInfo();
+ parseInfo.Init();
+
+ parseInfo.calendar = dtfi.Calendar;
+ parseInfo.fAllowInnerWhite = ((styles & DateTimeStyles.AllowInnerWhite) != 0);
+ parseInfo.fAllowTrailingWhite = ((styles & DateTimeStyles.AllowTrailingWhite) != 0);
+
+ // We need the original values of the following two below.
+ String originalFormat = formatParam;
+
+ if (formatParam.Length == 1)
+ {
+ if (((result.flags & ParseFlags.CaptureOffset) != 0) && formatParam[0] == 'U')
+ {
+ // The 'U' format is not allowed for DateTimeOffset
+ result.SetFailure(ParseFailureKind.Format, "Format_BadFormatSpecifier", null);
+ return false;
+ }
+ formatParam = ExpandPredefinedFormat(formatParam, ref dtfi, ref parseInfo, ref result);
+ }
+
+ bool bTimeOnly = false;
+ result.calendar = parseInfo.calendar;
+
+ if (parseInfo.calendar.ID == CalendarId.HEBREW)
+ {
+ parseInfo.parseNumberDelegate = m_hebrewNumberParser;
+ parseInfo.fCustomNumberParser = true;
+ }
+
+ // Reset these values to negative one so that we could throw exception
+ // if we have parsed every item twice.
+ result.Hour = result.Minute = result.Second = -1;
+
+ __DTString format = new __DTString(formatParam, dtfi, false);
+ __DTString str = new __DTString(s, dtfi, false);
+
+ if (parseInfo.fAllowTrailingWhite)
+ {
+ // Trim trailing spaces if AllowTrailingWhite.
+ format.TrimTail();
+ format.RemoveTrailingInQuoteSpaces();
+ str.TrimTail();
+ }
+
+ if ((styles & DateTimeStyles.AllowLeadingWhite) != 0)
+ {
+ format.SkipWhiteSpaces();
+ format.RemoveLeadingInQuoteSpaces();
+ str.SkipWhiteSpaces();
+ }
+
+ //
+ // Scan every character in format and match the pattern in str.
+ //
+ while (format.GetNext())
+ {
+ // We trim inner spaces here, so that we will not eat trailing spaces when
+ // AllowTrailingWhite is not used.
+ if (parseInfo.fAllowInnerWhite)
+ {
+ str.SkipWhiteSpaces();
+ }
+ if (!ParseByFormat(ref str, ref format, ref parseInfo, dtfi, ref result))
+ {
+ return (false);
+ }
+ }
+
+ if (str.Index < str.Value.Length - 1)
+ {
+ // There are still remaining character in str.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+
+ if (parseInfo.fUseTwoDigitYear && ((dtfi.FormatFlags & DateTimeFormatFlags.UseHebrewRule) == 0))
+ {
+ // A two digit year value is expected. Check if the parsed year value is valid.
+ if (result.Year >= 100)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ try
+ {
+ result.Year = parseInfo.calendar.ToFourDigitYear(result.Year);
+ }
+ catch (ArgumentOutOfRangeException e)
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", e);
+ return false;
+ }
+ }
+
+ if (parseInfo.fUseHour12)
+ {
+ if (parseInfo.timeMark == TM.NotSet)
+ {
+ // hh is used, but no AM/PM designator is specified.
+ // Assume the time is AM.
+ // Don't throw exceptions in here becasue it is very confusing for the caller.
+ // I always got confused myself when I use "hh:mm:ss" to parse a time string,
+ // and ParseExact() throws on me (because I didn't use the 24-hour clock 'HH').
+ parseInfo.timeMark = TM.AM;
+ }
+ if (result.Hour > 12)
+ {
+ // AM/PM is used, but the value for HH is too big.
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ if (parseInfo.timeMark == TM.AM)
+ {
+ if (result.Hour == 12)
+ {
+ result.Hour = 0;
+ }
+ }
+ else
+ {
+ result.Hour = (result.Hour == 12) ? 12 : result.Hour + 12;
+ }
+ }
+ else
+ {
+ // Military (24-hour time) mode
+ //
+ // AM cannot be set with a 24-hour time like 17:15.
+ // PM cannot be set with a 24-hour time like 03:15.
+ if ((parseInfo.timeMark == TM.AM && result.Hour >= 12)
+ || (parseInfo.timeMark == TM.PM && result.Hour < 12))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDateTime", null);
+ return false;
+ }
+ }
+
+
+ // Check if the parased string only contains hour/minute/second values.
+ bTimeOnly = (result.Year == -1 && result.Month == -1 && result.Day == -1);
+ if (!CheckDefaultDateTime(ref result, ref parseInfo.calendar, styles))
+ {
+ return false;
+ }
+
+ if (!bTimeOnly && dtfi.HasYearMonthAdjustment)
+ {
+ if (!dtfi.YearMonthAdjustment(ref result.Year, ref result.Month, ((result.flags & ParseFlags.ParsedMonthName) != 0)))
+ {
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ return false;
+ }
+ }
+ if (!parseInfo.calendar.TryToDateTime(result.Year, result.Month, result.Day,
+ result.Hour, result.Minute, result.Second, 0, result.era, out result.parsedDate))
+ {
+ result.SetFailure(ParseFailureKind.FormatBadDateTimeCalendar, "Format_BadDateTimeCalendar", null);
+ return false;
+ }
+ if (result.fraction > 0)
+ {
+ result.parsedDate = result.parsedDate.AddTicks((long)Math.Round(result.fraction * Calendar.TicksPerSecond));
+ }
+
+ //
+ // We have to check day of week before we adjust to the time zone.
+ // It is because the value of day of week may change after adjusting
+ // to the time zone.
+ //
+ if (parseInfo.dayOfWeek != -1)
+ {
+ //
+ // Check if day of week is correct.
+ //
+ if (parseInfo.dayOfWeek != (int)parseInfo.calendar.GetDayOfWeek(result.parsedDate))
+ {
+ result.SetFailure(ParseFailureKind.Format, "Format_BadDayOfWeek", null);
+ return false;
+ }
+ }
+
+
+ if (!DetermineTimeZoneAdjustments(ref result, styles, bTimeOnly))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private static Exception GetDateTimeParseException(ref DateTimeResult result)
+ {
+ switch (result.failure)
+ {
+ case ParseFailureKind.ArgumentNull:
+ return new ArgumentNullException(result.failureArgumentName, SR.GetResourceString(result.failureMessageID));
+ case ParseFailureKind.Format:
+ return new FormatException(SR.GetResourceString(result.failureMessageID));
+ case ParseFailureKind.FormatWithParameter:
+ return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), result.failureMessageFormatArgument));
+ case ParseFailureKind.FormatBadDateTimeCalendar:
+ return new FormatException(SR.Format(SR.GetResourceString(result.failureMessageID), result.calendar));
+ default:
+ Debug.Assert(false, "Unkown DateTimeParseFailure: " + result);
+ return null;
+ }
+ }
+
+ // Builds with _LOGGING defined (x86dbg, amd64chk, etc) support tracing
+ // Set the following internal-only/unsupported environment variables to enable DateTime tracing to the console:
+ //
+ // COMPlus_LogEnable=1
+ // COMPlus_LogToConsole=1
+ // COMPlus_LogLevel=9
+ // COMPlus_ManagedLogFacility=0x00001000
+ [Pure]
+ [Conditional("_LOGGING")]
+ internal static void LexTraceExit(string message, DS dps)
+ {
+#if _LOGGING
+ if (!_tracingEnabled)
+ return;
+ BCLDebug.Trace("DATETIME", "[DATETIME] Lex return {0}, DS.{1}", message, dps);
+#endif // _LOGGING
+ }
+ [Pure]
+ [Conditional("_LOGGING")]
+ internal static void PTSTraceExit(DS dps, bool passed)
+ {
+#if _LOGGING
+ if (!_tracingEnabled)
+ return;
+ BCLDebug.Trace("DATETIME", "[DATETIME] ProcessTerminalState {0} @ DS.{1}", passed ? "passed" : "failed", dps);
+#endif // _LOGGING
+ }
+ [Pure]
+ [Conditional("_LOGGING")]
+ internal static void TPTraceExit(string message, DS dps)
+ {
+#if _LOGGING
+ if (!_tracingEnabled)
+ return;
+ BCLDebug.Trace("DATETIME", "[DATETIME] TryParse return {0}, DS.{1}", message, dps);
+#endif // _LOGGING
+ }
+ [Pure]
+ [Conditional("_LOGGING")]
+ internal static void DTFITrace(DateTimeFormatInfo dtfi)
+ {
+#if _LOGGING
+ if (!_tracingEnabled)
+ return;
+
+ BCLDebug.Trace("DATETIME", "[DATETIME] DateTimeFormatInfo Properties");
+#if !FEATURE_COREFX_GLOBALIZATION
+ BCLDebug.Trace("DATETIME", " NativeCalendarName {0}", Hex(dtfi.NativeCalendarName));
+#endif
+ BCLDebug.Trace("DATETIME", " AMDesignator {0}", Hex(dtfi.AMDesignator));
+ BCLDebug.Trace("DATETIME", " PMDesignator {0}", Hex(dtfi.PMDesignator));
+ BCLDebug.Trace("DATETIME", " TimeSeparator {0}", Hex(dtfi.TimeSeparator));
+ BCLDebug.Trace("DATETIME", " AbbrvDayNames {0}", Hex(dtfi.AbbreviatedDayNames));
+ BCLDebug.Trace("DATETIME", " ShortestDayNames {0}", Hex(dtfi.ShortestDayNames));
+ BCLDebug.Trace("DATETIME", " DayNames {0}", Hex(dtfi.DayNames));
+ BCLDebug.Trace("DATETIME", " AbbrvMonthNames {0}", Hex(dtfi.AbbreviatedMonthNames));
+ BCLDebug.Trace("DATETIME", " MonthNames {0}", Hex(dtfi.MonthNames));
+ BCLDebug.Trace("DATETIME", " AbbrvMonthGenNames {0}", Hex(dtfi.AbbreviatedMonthGenitiveNames));
+ BCLDebug.Trace("DATETIME", " MonthGenNames {0}", Hex(dtfi.MonthGenitiveNames));
+#endif // _LOGGING
+ }
+#if _LOGGING
+ [Pure]
+ // return a string in the form: "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ internal static string Hex(string[] strs)
+ {
+ if (strs == null || strs.Length == 0)
+ return String.Empty;
+ if (strs.Length == 1)
+ return Hex(strs[0]);
+
+ int curLineLength = 0;
+ int maxLineLength = 55;
+ int newLinePadding = 20;
+
+
+ //invariant: strs.Length >= 2
+ StringBuilder buffer = new StringBuilder();
+ buffer.Append(Hex(strs[0]));
+ curLineLength = buffer.Length;
+ String s;
+
+ for (int i = 1; i < strs.Length - 1; i++)
+ {
+ s = Hex(strs[i]);
+
+ if (s.Length > maxLineLength || (curLineLength + s.Length + 2) > maxLineLength)
+ {
+ buffer.Append(',');
+ buffer.Append(Environment.NewLine);
+ buffer.Append(' ', newLinePadding);
+ curLineLength = 0;
+ }
+ else
+ {
+ buffer.Append(", ");
+ curLineLength += 2;
+ }
+ buffer.Append(s);
+ curLineLength += s.Length;
+ }
+
+ buffer.Append(',');
+ s = Hex(strs[strs.Length - 1]);
+ if (s.Length > maxLineLength || (curLineLength + s.Length + 6) > maxLineLength)
+ {
+ buffer.Append(Environment.NewLine);
+ buffer.Append(' ', newLinePadding);
+ }
+ else
+ {
+ buffer.Append(' ');
+ }
+ buffer.Append(s);
+ return buffer.ToString();
+ }
+ [Pure]
+ // return a string in the form: "Sun"
+ internal static string Hex(string str)
+ {
+ StringBuilder buffer = new StringBuilder();
+ buffer.Append("\"");
+ for (int i = 0; i < str.Length; i++)
+ {
+ if (str[i] <= '\x007f')
+ buffer.Append(str[i]);
+ else
+ buffer.Append("\\u" + ((int)str[i]).ToString("x4", CultureInfo.InvariantCulture));
+ }
+ buffer.Append("\"");
+ return buffer.ToString();
+ }
+ [Pure]
+ // return an unicode escaped string form of char c
+ internal static String Hex(char c)
+ {
+ if (c <= '\x007f')
+ return c.ToString(CultureInfo.InvariantCulture);
+ else
+ return "\\u" + ((int)c).ToString("x4", CultureInfo.InvariantCulture);
+ }
+
+ internal static bool _tracingEnabled = BCLDebug.CheckEnabled("DATETIME");
+#endif // _LOGGING
+ }
+
+
+ //
+ // This is a string parsing helper which wraps a String object.
+ // It has a Index property which tracks
+ // the current parsing pointer of the string.
+ //
+ internal
+ struct __DTString
+ {
+ //
+ // Value propery: stores the real string to be parsed.
+ //
+ internal String Value;
+
+ //
+ // Index property: points to the character that we are currently parsing.
+ //
+ internal int Index;
+
+ // The length of Value string.
+ internal int len;
+
+ // The current chracter to be looked at.
+ internal char m_current;
+
+ private CompareInfo m_info;
+ // Flag to indicate if we encouter an digit, we should check for token or not.
+ // In some cultures, such as mn-MN, it uses "\x0031\x00a0\x0434\x04af\x0433\x044d\x044d\x0440\x00a0\x0441\x0430\x0440" in month names.
+ private bool m_checkDigitToken;
+
+ internal __DTString(String str, DateTimeFormatInfo dtfi, bool checkDigitToken) : this(str, dtfi)
+ {
+ m_checkDigitToken = checkDigitToken;
+ }
+
+ internal __DTString(String str, DateTimeFormatInfo dtfi)
+ {
+ Index = -1;
+ Value = str;
+ len = Value.Length;
+
+ m_current = '\0';
+ if (dtfi != null)
+ {
+ m_info = dtfi.CompareInfo;
+ m_checkDigitToken = ((dtfi.FormatFlags & DateTimeFormatFlags.UseDigitPrefixInTokens) != 0);
+ }
+ else
+ {
+ m_info = CultureInfo.CurrentCulture.CompareInfo;
+ m_checkDigitToken = false;
+ }
+ }
+
+ internal CompareInfo CompareInfo
+ {
+ get { return m_info; }
+ }
+
+ //
+ // Advance the Index.
+ // Return true if Index is NOT at the end of the string.
+ //
+ // Typical usage:
+ // while (str.GetNext())
+ // {
+ // char ch = str.GetChar()
+ // }
+ internal bool GetNext()
+ {
+ Index++;
+ if (Index < len)
+ {
+ m_current = Value[Index];
+ return (true);
+ }
+ return (false);
+ }
+
+ internal bool AtEnd()
+ {
+ return Index < len ? false : true;
+ }
+
+ internal bool Advance(int count)
+ {
+ Debug.Assert(Index + count <= len, "__DTString::Advance: Index + count <= len");
+ Index += count;
+ if (Index < len)
+ {
+ m_current = Value[Index];
+ return (true);
+ }
+ return (false);
+ }
+
+
+ // Used by DateTime.Parse() to get the next token.
+ internal void GetRegularToken(out TokenType tokenType, out int tokenValue, DateTimeFormatInfo dtfi)
+ {
+ tokenValue = 0;
+ if (Index >= len)
+ {
+ tokenType = TokenType.EndOfString;
+ return;
+ }
+
+ tokenType = TokenType.UnknownToken;
+
+ Start:
+ if (DateTimeParse.IsDigit(m_current))
+ {
+ // This is a digit.
+ tokenValue = m_current - '0';
+ int value;
+ int start = Index;
+
+ //
+ // Collect other digits.
+ //
+ while (++Index < len)
+ {
+ m_current = Value[Index];
+ value = m_current - '0';
+ if (value >= 0 && value <= 9)
+ {
+ tokenValue = tokenValue * 10 + value;
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (Index - start > DateTimeParse.MaxDateTimeNumberDigits)
+ {
+ tokenType = TokenType.NumberToken;
+ tokenValue = -1;
+ }
+ else if (Index - start < 3)
+ {
+ tokenType = TokenType.NumberToken;
+ }
+ else
+ {
+ // If there are more than 3 digits, assume that it's a year value.
+ tokenType = TokenType.YearNumberToken;
+ }
+ if (m_checkDigitToken)
+ {
+ int save = Index;
+ char saveCh = m_current;
+ // Re-scan using the staring Index to see if this is a token.
+ Index = start; // To include the first digit.
+ m_current = Value[Index];
+ TokenType tempType;
+ int tempValue;
+ // This DTFI has tokens starting with digits.
+ // E.g. mn-MN has month name like "\x0031\x00a0\x0434\x04af\x0433\x044d\x044d\x0440\x00a0\x0441\x0430\x0440"
+ if (dtfi.Tokenize(TokenType.RegularTokenMask, out tempType, out tempValue, ref this))
+ {
+ tokenType = tempType;
+ tokenValue = tempValue;
+ // This is a token, so the Index has been advanced propertly in DTFI.Tokenizer().
+ }
+ else
+ {
+ // Use the number token value.
+ // Restore the index.
+ Index = save;
+ m_current = saveCh;
+ }
+ }
+ }
+ else if (Char.IsWhiteSpace(m_current))
+ {
+ // Just skip to the next character.
+ while (++Index < len)
+ {
+ m_current = Value[Index];
+ if (!(Char.IsWhiteSpace(m_current)))
+ {
+ goto Start;
+ }
+ }
+ // We have reached the end of string.
+ tokenType = TokenType.EndOfString;
+ }
+ else
+ {
+ dtfi.Tokenize(TokenType.RegularTokenMask, out tokenType, out tokenValue, ref this);
+ }
+ }
+
+ internal TokenType GetSeparatorToken(DateTimeFormatInfo dtfi, out int indexBeforeSeparator, out char charBeforeSeparator)
+ {
+ indexBeforeSeparator = Index;
+ charBeforeSeparator = m_current;
+ TokenType tokenType;
+ if (!SkipWhiteSpaceCurrent())
+ {
+ // Reach the end of the string.
+ return (TokenType.SEP_End);
+ }
+ if (!DateTimeParse.IsDigit(m_current))
+ {
+ // Not a digit. Tokenize it.
+ int tokenValue;
+ bool found = dtfi.Tokenize(TokenType.SeparatorTokenMask, out tokenType, out tokenValue, ref this);
+ if (!found)
+ {
+ tokenType = TokenType.SEP_Space;
+ }
+ }
+ else
+ {
+ // Do nothing here. If we see a number, it will not be a separator. There is no need wasting time trying to find the
+ // separator token.
+ tokenType = TokenType.SEP_Space;
+ }
+ return (tokenType);
+ }
+
+ internal bool MatchSpecifiedWord(String target)
+ {
+ return MatchSpecifiedWord(target, target.Length + Index);
+ }
+
+ internal bool MatchSpecifiedWord(String target, int endIndex)
+ {
+ int count = endIndex - Index;
+
+ if (count != target.Length)
+ {
+ return false;
+ }
+
+ if (Index + count > len)
+ {
+ return false;
+ }
+
+ return (m_info.Compare(Value, Index, count, target, 0, count, CompareOptions.IgnoreCase) == 0);
+ }
+
+ private static Char[] WhiteSpaceChecks = new Char[] { ' ', '\u00A0' };
+
+ internal bool MatchSpecifiedWords(String target, bool checkWordBoundary, ref int matchLength)
+ {
+ int valueRemaining = Value.Length - Index;
+ matchLength = target.Length;
+
+ if (matchLength > valueRemaining || m_info.Compare(Value, Index, matchLength, target, 0, matchLength, CompareOptions.IgnoreCase) != 0)
+ {
+ // Check word by word
+ int targetPosition = 0; // Where we are in the target string
+ int thisPosition = Index; // Where we are in this string
+ int wsIndex = target.IndexOfAny(WhiteSpaceChecks, targetPosition);
+ if (wsIndex == -1)
+ {
+ return false;
+ }
+ do
+ {
+ int segmentLength = wsIndex - targetPosition;
+ if (thisPosition >= Value.Length - segmentLength)
+ { // Subtraction to prevent overflow.
+ return false;
+ }
+ if (segmentLength == 0)
+ {
+ // If segmentLength == 0, it means that we have leading space in the target string.
+ // In that case, skip the leading spaces in the target and this string.
+ matchLength--;
+ }
+ else
+ {
+ // Make sure we also have whitespace in the input string
+ if (!Char.IsWhiteSpace(Value[thisPosition + segmentLength]))
+ {
+ return false;
+ }
+ if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) != 0)
+ {
+ return false;
+ }
+ // Advance the input string
+ thisPosition = thisPosition + segmentLength + 1;
+ }
+ // Advance our target string
+ targetPosition = wsIndex + 1;
+
+
+ // Skip past multiple whitespace
+ while (thisPosition < Value.Length && Char.IsWhiteSpace(Value[thisPosition]))
+ {
+ thisPosition++;
+ matchLength++;
+ }
+ } while ((wsIndex = target.IndexOfAny(WhiteSpaceChecks, targetPosition)) >= 0);
+ // now check the last segment;
+ if (targetPosition < target.Length)
+ {
+ int segmentLength = target.Length - targetPosition;
+ if (thisPosition > Value.Length - segmentLength)
+ {
+ return false;
+ }
+ if (m_info.Compare(Value, thisPosition, segmentLength, target, targetPosition, segmentLength, CompareOptions.IgnoreCase) != 0)
+ {
+ return false;
+ }
+ }
+ }
+
+ if (checkWordBoundary)
+ {
+ int nextCharIndex = Index + matchLength;
+ if (nextCharIndex < Value.Length)
+ {
+ if (Char.IsLetter(Value[nextCharIndex]))
+ {
+ return (false);
+ }
+ }
+ }
+ return (true);
+ }
+
+ //
+ // Check to see if the string starting from Index is a prefix of
+ // str.
+ // If a match is found, true value is returned and Index is updated to the next character to be parsed.
+ // Otherwise, Index is unchanged.
+ //
+ internal bool Match(String str)
+ {
+ if (++Index >= len)
+ {
+ return (false);
+ }
+
+ if (str.Length > (Value.Length - Index))
+ {
+ return false;
+ }
+
+ if (m_info.Compare(Value, Index, str.Length, str, 0, str.Length, CompareOptions.Ordinal) == 0)
+ {
+ // Update the Index to the end of the matching string.
+ // So the following GetNext()/Match() opeartion will get
+ // the next character to be parsed.
+ Index += (str.Length - 1);
+ return (true);
+ }
+ return (false);
+ }
+
+ internal bool Match(char ch)
+ {
+ if (++Index >= len)
+ {
+ return (false);
+ }
+ if (Value[Index] == ch)
+ {
+ m_current = ch;
+ return (true);
+ }
+ Index--;
+ return (false);
+ }
+
+ //
+ // Actions: From the current position, try matching the longest word in the specified string array.
+ // E.g. words[] = {"AB", "ABC", "ABCD"}, if the current position points to a substring like "ABC DEF",
+ // MatchLongestWords(words, ref MaxMatchStrLen) will return 1 (the index), and maxMatchLen will be 3.
+ // Returns:
+ // The index that contains the longest word to match
+ // Arguments:
+ // words The string array that contains words to search.
+ // maxMatchStrLen [in/out] the initailized maximum length. This parameter can be used to
+ // find the longest match in two string arrays.
+ //
+ internal int MatchLongestWords(String[] words, ref int maxMatchStrLen)
+ {
+ int result = -1;
+ for (int i = 0; i < words.Length; i++)
+ {
+ String word = words[i];
+ int matchLength = word.Length;
+ if (MatchSpecifiedWords(word, false, ref matchLength))
+ {
+ if (matchLength > maxMatchStrLen)
+ {
+ maxMatchStrLen = matchLength;
+ result = i;
+ }
+ }
+ }
+
+ return (result);
+ }
+
+ //
+ // Get the number of repeat character after the current character.
+ // For a string "hh:mm:ss" at Index of 3. GetRepeatCount() = 2, and Index
+ // will point to the second ':'.
+ //
+ internal int GetRepeatCount()
+ {
+ char repeatChar = Value[Index];
+ int pos = Index + 1;
+ while ((pos < len) && (Value[pos] == repeatChar))
+ {
+ pos++;
+ }
+ int repeatCount = (pos - Index);
+ // Update the Index to the end of the repeated characters.
+ // So the following GetNext() opeartion will get
+ // the next character to be parsed.
+ Index = pos - 1;
+ return (repeatCount);
+ }
+
+ // Return false when end of string is encountered or a non-digit character is found.
+ internal bool GetNextDigit()
+ {
+ if (++Index >= len)
+ {
+ return (false);
+ }
+ return (DateTimeParse.IsDigit(Value[Index]));
+ }
+
+ //
+ // Get the current character.
+ //
+ internal char GetChar()
+ {
+ Debug.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len");
+ return (Value[Index]);
+ }
+
+ //
+ // Convert the current character to a digit, and return it.
+ //
+ internal int GetDigit()
+ {
+ Debug.Assert(Index >= 0 && Index < len, "Index >= 0 && Index < len");
+ Debug.Assert(DateTimeParse.IsDigit(Value[Index]), "IsDigit(Value[Index])");
+ return (Value[Index] - '0');
+ }
+
+ //
+ // Eat White Space ahead of the current position
+ //
+ // Return false if end of string is encountered.
+ //
+ internal void SkipWhiteSpaces()
+ {
+ // Look ahead to see if the next character
+ // is a whitespace.
+ while (Index + 1 < len)
+ {
+ char ch = Value[Index + 1];
+ if (!Char.IsWhiteSpace(ch))
+ {
+ return;
+ }
+ Index++;
+ }
+ return;
+ }
+
+ //
+ // Skip white spaces from the current position
+ //
+ // Return false if end of string is encountered.
+ //
+ internal bool SkipWhiteSpaceCurrent()
+ {
+ if (Index >= len)
+ {
+ return (false);
+ }
+
+ if (!Char.IsWhiteSpace(m_current))
+ {
+ return (true);
+ }
+
+ while (++Index < len)
+ {
+ m_current = Value[Index];
+ if (!Char.IsWhiteSpace(m_current))
+ {
+ return (true);
+ }
+ // Nothing here.
+ }
+ return (false);
+ }
+
+ internal void TrimTail()
+ {
+ int i = len - 1;
+ while (i >= 0 && Char.IsWhiteSpace(Value[i]))
+ {
+ i--;
+ }
+ Value = Value.Substring(0, i + 1);
+ len = Value.Length;
+ }
+
+ // Trim the trailing spaces within a quoted string.
+ // Call this after TrimTail() is done.
+ internal void RemoveTrailingInQuoteSpaces()
+ {
+ int i = len - 1;
+ if (i <= 1)
+ {
+ return;
+ }
+ char ch = Value[i];
+ // Check if the last character is a quote.
+ if (ch == '\'' || ch == '\"')
+ {
+ if (Char.IsWhiteSpace(Value[i - 1]))
+ {
+ i--;
+ while (i >= 1 && Char.IsWhiteSpace(Value[i - 1]))
+ {
+ i--;
+ }
+ Value = Value.Remove(i, Value.Length - 1 - i);
+ len = Value.Length;
+ }
+ }
+ }
+
+ // Trim the leading spaces within a quoted string.
+ // Call this after the leading spaces before quoted string are trimmed.
+ internal void RemoveLeadingInQuoteSpaces()
+ {
+ if (len <= 2)
+ {
+ return;
+ }
+ int i = 0;
+ char ch = Value[i];
+ // Check if the last character is a quote.
+ if (ch == '\'' || ch == '\"')
+ {
+ while ((i + 1) < len && Char.IsWhiteSpace(Value[i + 1]))
+ {
+ i++;
+ }
+ if (i != 0)
+ {
+ Value = Value.Remove(1, i);
+ len = Value.Length;
+ }
+ }
+ }
+
+ internal DTSubString GetSubString()
+ {
+ DTSubString sub = new DTSubString();
+ sub.index = Index;
+ sub.s = Value;
+ while (Index + sub.length < len)
+ {
+ DTSubStringType currentType;
+ Char ch = Value[Index + sub.length];
+ if (ch >= '0' && ch <= '9')
+ {
+ currentType = DTSubStringType.Number;
+ }
+ else
+ {
+ currentType = DTSubStringType.Other;
+ }
+
+ if (sub.length == 0)
+ {
+ sub.type = currentType;
+ }
+ else
+ {
+ if (sub.type != currentType)
+ {
+ break;
+ }
+ }
+ sub.length++;
+ if (currentType == DTSubStringType.Number)
+ {
+ // Incorporate the number into the value
+ // Limit the digits to prevent overflow
+ if (sub.length > DateTimeParse.MaxDateTimeNumberDigits)
+ {
+ sub.type = DTSubStringType.Invalid;
+ return sub;
+ }
+ int number = ch - '0';
+ Debug.Assert(number >= 0 && number <= 9, "number >= 0 && number <= 9");
+ sub.value = sub.value * 10 + number;
+ }
+ else
+ {
+ // For non numbers, just return this length 1 token. This should be expanded
+ // to more types of thing if this parsing approach is used for things other
+ // than numbers and single characters
+ break;
+ }
+ }
+ if (sub.length == 0)
+ {
+ sub.type = DTSubStringType.End;
+ return sub;
+ }
+
+ return sub;
+ }
+
+ internal void ConsumeSubString(DTSubString sub)
+ {
+ Debug.Assert(sub.index == Index, "sub.index == Index");
+ Debug.Assert(sub.index + sub.length <= len, "sub.index + sub.length <= len");
+ Index = sub.index + sub.length;
+ if (Index < len)
+ {
+ m_current = Value[Index];
+ }
+ }
+ }
+
+ internal enum DTSubStringType
+ {
+ Unknown = 0,
+ Invalid = 1,
+ Number = 2,
+ End = 3,
+ Other = 4,
+ }
+
+ internal struct DTSubString
+ {
+ internal String s;
+ internal Int32 index;
+ internal Int32 length;
+ internal DTSubStringType type;
+ internal Int32 value;
+
+ internal Char this[Int32 relativeIndex]
+ {
+ get
+ {
+ return s[index + relativeIndex];
+ }
+ }
+ }
+
+ //
+ // The buffer to store the parsing token.
+ //
+ internal
+ struct DateTimeToken
+ {
+ internal DateTimeParse.DTT dtt; // Store the token
+ internal TokenType suffix; // Store the CJK Year/Month/Day suffix (if any)
+ internal int num; // Store the number that we are parsing (if any)
+ }
+
+ //
+ // The buffer to store temporary parsing information.
+ //
+ internal
+ unsafe struct DateTimeRawInfo
+ {
+ private int* num;
+ internal int numCount;
+ internal int month;
+ internal int year;
+ internal int dayOfWeek;
+ internal int era;
+ internal DateTimeParse.TM timeMark;
+ internal double fraction;
+ internal bool hasSameDateAndTimeSeparators;
+
+ internal void Init(int* numberBuffer)
+ {
+ month = -1;
+ year = -1;
+ dayOfWeek = -1;
+ era = -1;
+ timeMark = DateTimeParse.TM.NotSet;
+ fraction = -1;
+ num = numberBuffer;
+ }
+ internal unsafe void AddNumber(int value)
+ {
+ num[numCount++] = value;
+ }
+ internal unsafe int GetNumber(int index)
+ {
+ return num[index];
+ }
+ }
+
+ internal enum ParseFailureKind
+ {
+ None = 0,
+ ArgumentNull = 1,
+ Format = 2,
+ FormatWithParameter = 3,
+ FormatBadDateTimeCalendar = 4, // FormatException when ArgumentOutOfRange is thrown by a Calendar.TryToDateTime().
+ };
+
+ [Flags]
+ internal enum ParseFlags
+ {
+ HaveYear = 0x00000001,
+ HaveMonth = 0x00000002,
+ HaveDay = 0x00000004,
+ HaveHour = 0x00000008,
+ HaveMinute = 0x00000010,
+ HaveSecond = 0x00000020,
+ HaveTime = 0x00000040,
+ HaveDate = 0x00000080,
+ TimeZoneUsed = 0x00000100,
+ TimeZoneUtc = 0x00000200,
+ ParsedMonthName = 0x00000400,
+ CaptureOffset = 0x00000800,
+ YearDefault = 0x00001000,
+ Rfc1123Pattern = 0x00002000,
+ UtcSortPattern = 0x00004000,
+ }
+
+ //
+ // This will store the result of the parsing. And it will be eventually
+ // used to construct a DateTime instance.
+ //
+ internal
+ struct DateTimeResult
+ {
+ internal int Year;
+ internal int Month;
+ internal int Day;
+ //
+ // Set time defualt to 00:00:00.
+ //
+ internal int Hour;
+ internal int Minute;
+ internal int Second;
+ internal double fraction;
+
+ internal int era;
+
+ internal ParseFlags flags;
+
+ internal TimeSpan timeZoneOffset;
+
+ internal Calendar calendar;
+
+ internal DateTime parsedDate;
+
+ internal ParseFailureKind failure;
+ internal string failureMessageID;
+ internal object failureMessageFormatArgument;
+ internal string failureArgumentName;
+
+ internal void Init()
+ {
+ Year = -1;
+ Month = -1;
+ Day = -1;
+ fraction = -1;
+ era = -1;
+ }
+
+ internal void SetDate(int year, int month, int day)
+ {
+ Year = year;
+ Month = month;
+ Day = day;
+ }
+ internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument)
+ {
+ this.failure = failure;
+ this.failureMessageID = failureMessageID;
+ this.failureMessageFormatArgument = failureMessageFormatArgument;
+ }
+
+ internal void SetFailure(ParseFailureKind failure, string failureMessageID, object failureMessageFormatArgument, string failureArgumentName)
+ {
+ this.failure = failure;
+ this.failureMessageID = failureMessageID;
+ this.failureMessageFormatArgument = failureMessageFormatArgument;
+ this.failureArgumentName = failureArgumentName;
+ }
+ }
+
+ // This is the helper data structure used in ParseExact().
+ internal struct ParsingInfo
+ {
+ internal Calendar calendar;
+ internal int dayOfWeek;
+ internal DateTimeParse.TM timeMark;
+
+ internal bool fUseHour12;
+ internal bool fUseTwoDigitYear;
+ internal bool fAllowInnerWhite;
+ internal bool fAllowTrailingWhite;
+ internal bool fCustomNumberParser;
+ internal DateTimeParse.MatchNumberDelegate parseNumberDelegate;
+
+ internal void Init()
+ {
+ dayOfWeek = -1;
+ timeMark = DateTimeParse.TM.NotSet;
+ }
+ }
+
+ //
+ // The type of token that will be returned by DateTimeFormatInfo.Tokenize().
+ //
+ internal enum TokenType
+ {
+ // The valid token should start from 1.
+
+ // Regular tokens. The range is from 0x00 ~ 0xff.
+ NumberToken = 1, // The number. E.g. "12"
+ YearNumberToken = 2, // The number which is considered as year number, which has 3 or more digits. E.g. "2003"
+ Am = 3, // AM timemark. E.g. "AM"
+ Pm = 4, // PM timemark. E.g. "PM"
+ MonthToken = 5, // A word (or words) that represents a month name. E.g. "March"
+ EndOfString = 6, // End of string
+ DayOfWeekToken = 7, // A word (or words) that represents a day of week name. E.g. "Monday" or "Mon"
+ TimeZoneToken = 8, // A word that represents a timezone name. E.g. "GMT"
+ EraToken = 9, // A word that represents a era name. E.g. "A.D."
+ DateWordToken = 10, // A word that can appear in a DateTime string, but serves no parsing semantics. E.g. "de" in Spanish culture.
+ UnknownToken = 11, // An unknown word, which signals an error in parsing.
+ HebrewNumber = 12, // A number that is composed of Hebrew text. Hebrew calendar uses Hebrew digits for year values, month values, and day values.
+ JapaneseEraToken = 13, // Era name for JapaneseCalendar
+ TEraToken = 14, // Era name for TaiwanCalendar
+ IgnorableSymbol = 15, // A separator like "," that is equivalent to whitespace
+
+
+ // Separator tokens.
+ SEP_Unk = 0x100, // Unknown separator.
+ SEP_End = 0x200, // The end of the parsing string.
+ SEP_Space = 0x300, // Whitespace (including comma).
+ SEP_Am = 0x400, // AM timemark. E.g. "AM"
+ SEP_Pm = 0x500, // PM timemark. E.g. "PM"
+ SEP_Date = 0x600, // date separator. E.g. "/"
+ SEP_Time = 0x700, // time separator. E.g. ":"
+ SEP_YearSuff = 0x800, // Chinese/Japanese/Korean year suffix.
+ SEP_MonthSuff = 0x900, // Chinese/Japanese/Korean month suffix.
+ SEP_DaySuff = 0xa00, // Chinese/Japanese/Korean day suffix.
+ SEP_HourSuff = 0xb00, // Chinese/Japanese/Korean hour suffix.
+ SEP_MinuteSuff = 0xc00, // Chinese/Japanese/Korean minute suffix.
+ SEP_SecondSuff = 0xd00, // Chinese/Japanese/Korean second suffix.
+ SEP_LocalTimeMark = 0xe00, // 'T', used in ISO 8601 format.
+ SEP_DateOrOffset = 0xf00, // '-' which could be a date separator or start of a time zone offset
+
+ RegularTokenMask = 0x00ff,
+ SeparatorTokenMask = 0xff00,
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/DateTimeStyles.cs b/src/mscorlib/shared/System/Globalization/DateTimeStyles.cs
new file mode 100644
index 0000000000..79232ff199
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/DateTimeStyles.cs
@@ -0,0 +1,49 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Contains valid formats for DateTime recognized by
+** the DateTime class' parsing code.
+**
+**
+===========================================================*/
+
+namespace System.Globalization
+{
+ [Flags]
+ public enum DateTimeStyles
+ {
+ // Bit flag indicating that leading whitespace is allowed. Character values
+ // 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x0020 are considered to be
+ // whitespace.
+
+
+ None = 0x00000000,
+
+ AllowLeadingWhite = 0x00000001,
+
+ AllowTrailingWhite = 0x00000002, //Bitflag indicating trailing whitespace is allowed.
+
+ AllowInnerWhite = 0x00000004,
+
+ AllowWhiteSpaces = AllowLeadingWhite | AllowInnerWhite | AllowTrailingWhite,
+ // When parsing a date/time string, if all year/month/day are missing, set the default date
+ // to 0001/1/1, instead of the current year/month/day.
+
+ NoCurrentDateDefault = 0x00000008,
+ // When parsing a date/time string, if a timezone specifier ("GMT","Z","+xxxx", "-xxxx" exists), we will
+ // ajdust the parsed time based to GMT.
+
+ AdjustToUniversal = 0x00000010,
+
+ AssumeLocal = 0x00000020,
+
+ AssumeUniversal = 0x00000040,
+ // Attempt to preserve whether the input is unspecified, local or UTC
+ RoundtripKind = 0x00000080,
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/DaylightTime.cs b/src/mscorlib/shared/System/Globalization/DaylightTime.cs
new file mode 100644
index 0000000000..b3c70e1d10
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/DaylightTime.cs
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ // This class represents a starting/ending time for a period of daylight saving time.
+ [Serializable]
+ public class DaylightTime
+ {
+ private readonly DateTime _start;
+ private readonly DateTime _end;
+ private readonly TimeSpan _delta;
+
+ private DaylightTime()
+ {
+ }
+
+ public DaylightTime(DateTime start, DateTime end, TimeSpan delta)
+ {
+ _start = start;
+ _end = end;
+ _delta = delta;
+ }
+
+ // The start date of a daylight saving period.
+ public DateTime Start => _start;
+
+ // The end date of a daylight saving period.
+ public DateTime End => _end;
+
+ // Delta to stardard offset in ticks.
+ public TimeSpan Delta => _delta;
+ }
+
+ // Value type version of DaylightTime
+ internal struct DaylightTimeStruct
+ {
+ public DaylightTimeStruct(DateTime start, DateTime end, TimeSpan delta)
+ {
+ Start = start;
+ End = end;
+ Delta = delta;
+ }
+
+ public readonly DateTime Start;
+ public readonly DateTime End;
+ public readonly TimeSpan Delta;
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/DigitShapes.cs b/src/mscorlib/shared/System/Globalization/DigitShapes.cs
new file mode 100644
index 0000000000..1ce45dbeb6
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/DigitShapes.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ public enum DigitShapes : int
+ {
+ Context = 0x0000, // The shape depends on the previous text in the same output.
+ None = 0x0001, // Gives full Unicode compatibility.
+ NativeNational = 0x0002 // National shapes
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs
new file mode 100644
index 0000000000..d06b13cd7d
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/EastAsianLunisolarCalendar.cs
@@ -0,0 +1,710 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ [Serializable]
+ public abstract class EastAsianLunisolarCalendar : Calendar
+ {
+ internal const int LeapMonth = 0;
+ internal const int Jan1Month = 1;
+ internal const int Jan1Date = 2;
+ internal const int nDaysPerMonth = 3;
+
+ // # of days so far in the solar year
+ internal static readonly int[] DaysToMonth365 =
+ {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+ };
+
+ internal static readonly int[] DaysToMonth366 =
+ {
+ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
+ };
+
+ internal const int DatePartYear = 0;
+ internal const int DatePartDayOfYear = 1;
+ internal const int DatePartMonth = 2;
+ internal const int DatePartDay = 3;
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.LunisolarCalendar;
+ }
+ }
+
+ // Return the year number in the 60-year cycle.
+ //
+
+ public virtual int GetSexagenaryYear(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+
+ int year = 0, month = 0, day = 0;
+ TimeToLunar(time, ref year, ref month, ref day);
+
+ return ((year - 4) % 60) + 1;
+ }
+
+ // Return the celestial year from the 60-year cycle.
+ // The returned value is from 1 ~ 10.
+ //
+
+ public int GetCelestialStem(int sexagenaryYear)
+ {
+ if ((sexagenaryYear < 1) || (sexagenaryYear > 60))
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(sexagenaryYear),
+ SR.Format(SR.ArgumentOutOfRange_Range, 1, 60));
+ }
+ Contract.EndContractBlock();
+
+ return ((sexagenaryYear - 1) % 10) + 1;
+ }
+
+ // Return the Terrestial Branch from the the 60-year cycle.
+ // The returned value is from 1 ~ 12.
+ //
+
+ public int GetTerrestrialBranch(int sexagenaryYear)
+ {
+ if ((sexagenaryYear < 1) || (sexagenaryYear > 60))
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(sexagenaryYear),
+ SR.Format(SR.ArgumentOutOfRange_Range, 1, 60));
+ }
+ Contract.EndContractBlock();
+
+ return ((sexagenaryYear - 1) % 12) + 1;
+ }
+
+ internal abstract int GetYearInfo(int LunarYear, int Index);
+ internal abstract int GetYear(int year, DateTime time);
+ internal abstract int GetGregorianYear(int year, int era);
+
+ internal abstract int MinCalendarYear { get; }
+ internal abstract int MaxCalendarYear { get; }
+ internal abstract EraInfo[] CalEraInfo { get; }
+ internal abstract DateTime MinDate { get; }
+ internal abstract DateTime MaxDate { get; }
+
+ internal const int MaxCalendarMonth = 13;
+ internal const int MaxCalendarDay = 30;
+
+ internal int MinEraCalendarYear(int era)
+ {
+ EraInfo[] mEraInfo = CalEraInfo;
+ //ChineseLunisolarCalendar does not has m_EraInfo it is going to retuen null
+ if (mEraInfo == null)
+ {
+ return MinCalendarYear;
+ }
+
+ if (era == Calendar.CurrentEra)
+ {
+ era = CurrentEraValue;
+ }
+ //era has to be in the supported range otherwise we will throw exception in CheckEraRange()
+ if (era == GetEra(MinDate))
+ {
+ return (GetYear(MinCalendarYear, MinDate));
+ }
+
+ for (int i = 0; i < mEraInfo.Length; i++)
+ {
+ if (era == mEraInfo[i].era)
+ {
+ return (mEraInfo[i].minEraYear);
+ }
+ }
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+
+ internal int MaxEraCalendarYear(int era)
+ {
+ EraInfo[] mEraInfo = CalEraInfo;
+ //ChineseLunisolarCalendar does not has m_EraInfo it is going to retuen null
+ if (mEraInfo == null)
+ {
+ return MaxCalendarYear;
+ }
+
+ if (era == Calendar.CurrentEra)
+ {
+ era = CurrentEraValue;
+ }
+ //era has to be in the supported range otherwise we will throw exception in CheckEraRange()
+ if (era == GetEra(MaxDate))
+ {
+ return (GetYear(MaxCalendarYear, MaxDate));
+ }
+
+ for (int i = 0; i < mEraInfo.Length; i++)
+ {
+ if (era == mEraInfo[i].era)
+ {
+ return (mEraInfo[i].maxEraYear);
+ }
+ }
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+
+ internal EastAsianLunisolarCalendar()
+ {
+ }
+
+ internal void CheckTicksRange(long ticks)
+ {
+ if (ticks < MinSupportedDateTime.Ticks || ticks > MaxSupportedDateTime.Ticks)
+ {
+ throw new ArgumentOutOfRangeException(
+ "time",
+ String.Format(CultureInfo.InvariantCulture, SR.ArgumentOutOfRange_CalendarRange,
+ MinSupportedDateTime, MaxSupportedDateTime));
+ }
+ Contract.EndContractBlock();
+ }
+
+ internal void CheckEraRange(int era)
+ {
+ if (era == Calendar.CurrentEra)
+ {
+ era = CurrentEraValue;
+ }
+
+ if ((era < GetEra(MinDate)) || (era > GetEra(MaxDate)))
+ {
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+ }
+
+ internal int CheckYearRange(int year, int era)
+ {
+ CheckEraRange(era);
+ year = GetGregorianYear(year, era);
+
+ if ((year < MinCalendarYear) || (year > MaxCalendarYear))
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ SR.Format(SR.ArgumentOutOfRange_Range, MinEraCalendarYear(era), MaxEraCalendarYear(era)));
+ }
+ return year;
+ }
+
+ internal int CheckYearMonthRange(int year, int month, int era)
+ {
+ year = CheckYearRange(year, era);
+
+ if (month == 13)
+ {
+ //Reject if there is no leap month this year
+ if (GetYearInfo(year, LeapMonth) == 0)
+ throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
+ }
+
+ if (month < 1 || month > 13)
+ {
+ throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
+ }
+ return year;
+ }
+
+ internal int InternalGetDaysInMonth(int year, int month)
+ {
+ int nDays;
+ int mask; // mask for extracting bits
+
+ mask = 0x8000;
+ // convert the lunar day into a lunar month/date
+ mask >>= (month - 1);
+ if ((GetYearInfo(year, nDaysPerMonth) & mask) == 0)
+ nDays = 29;
+ else
+ nDays = 30;
+ return nDays;
+ }
+
+ // Returns the number of days in the month given by the year and
+ // month arguments.
+ //
+
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ year = CheckYearMonthRange(year, month, era);
+ return InternalGetDaysInMonth(year, month);
+ }
+
+ private static int GregorianIsLeapYear(int y)
+ {
+ return ((((y) % 4) != 0) ? 0 : ((((y) % 100) != 0) ? 1 : ((((y) % 400) != 0) ? 0 : 1)));
+ }
+
+ // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
+ //
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ year = CheckYearMonthRange(year, month, era);
+ int daysInMonth = InternalGetDaysInMonth(year, month);
+ if (day < 1 || day > daysInMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ SR.Format(SR.ArgumentOutOfRange_Day, daysInMonth, month));
+ }
+
+ int gy = 0; int gm = 0; int gd = 0;
+
+ if (LunarToGregorian(year, month, day, ref gy, ref gm, ref gd))
+ {
+ return new DateTime(gy, gm, gd, hour, minute, second, millisecond);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
+ }
+ }
+
+
+ //
+ // GregorianToLunar calculates lunar calendar info for the given gregorian year, month, date.
+ // The input date should be validated before calling this method.
+ //
+ internal void GregorianToLunar(int nSYear, int nSMonth, int nSDate, ref int nLYear, ref int nLMonth, ref int nLDate)
+ {
+ // unsigned int nLYear, nLMonth, nLDate; // lunar ymd
+ int nSolarDay; // day # in solar year
+ int nLunarDay; // day # in lunar year
+ int fLeap; // is it a solar leap year?
+ int LDpM; // lunar days/month bitfield
+ int mask; // mask for extracting bits
+ int nDays; // # days this lunar month
+ int nJan1Month, nJan1Date;
+
+ // calc the solar day of year
+ fLeap = GregorianIsLeapYear(nSYear);
+ nSolarDay = (fLeap == 1) ? DaysToMonth366[nSMonth - 1] : DaysToMonth365[nSMonth - 1];
+ nSolarDay += nSDate;
+
+ // init lunar year info
+ nLunarDay = nSolarDay;
+ nLYear = nSYear;
+ if (nLYear == (MaxCalendarYear + 1))
+ {
+ nLYear--;
+ nLunarDay += ((GregorianIsLeapYear(nLYear) == 1) ? 366 : 365);
+ nJan1Month = GetYearInfo(nLYear, Jan1Month);
+ nJan1Date = GetYearInfo(nLYear, Jan1Date);
+ }
+ else
+ {
+ nJan1Month = GetYearInfo(nLYear, Jan1Month);
+ nJan1Date = GetYearInfo(nLYear, Jan1Date);
+
+ // check if this solar date is actually part of the previous
+ // lunar year
+ if ((nSMonth < nJan1Month) ||
+ (nSMonth == nJan1Month && nSDate < nJan1Date))
+ {
+ // the corresponding lunar day is actually part of the previous
+ // lunar year
+ nLYear--;
+
+ // add a solar year to the lunar day #
+ nLunarDay += ((GregorianIsLeapYear(nLYear) == 1) ? 366 : 365);
+
+ // update the new start of year
+ nJan1Month = GetYearInfo(nLYear, Jan1Month);
+ nJan1Date = GetYearInfo(nLYear, Jan1Date);
+ }
+ }
+
+ // convert solar day into lunar day.
+ // subtract off the beginning part of the solar year which is not
+ // part of the lunar year. since this part is always in Jan or Feb,
+ // we don't need to handle Leap Year (LY only affects March
+ // and later).
+ nLunarDay -= DaysToMonth365[nJan1Month - 1];
+ nLunarDay -= (nJan1Date - 1);
+
+ // convert the lunar day into a lunar month/date
+ mask = 0x8000;
+ LDpM = GetYearInfo(nLYear, nDaysPerMonth);
+ nDays = ((LDpM & mask) != 0) ? 30 : 29;
+ nLMonth = 1;
+ while (nLunarDay > nDays)
+ {
+ nLunarDay -= nDays;
+ nLMonth++;
+ mask >>= 1;
+ nDays = ((LDpM & mask) != 0) ? 30 : 29;
+ }
+ nLDate = nLunarDay;
+ }
+
+ /*
+ //Convert from Lunar to Gregorian
+ //Highly inefficient, but it works based on the forward conversion
+ */
+ internal bool LunarToGregorian(int nLYear, int nLMonth, int nLDate, ref int nSolarYear, ref int nSolarMonth, ref int nSolarDay)
+ {
+ int numLunarDays;
+
+ if (nLDate < 1 || nLDate > 30)
+ return false;
+
+ numLunarDays = nLDate - 1;
+
+ //Add previous months days to form the total num of days from the first of the month.
+ for (int i = 1; i < nLMonth; i++)
+ {
+ numLunarDays += InternalGetDaysInMonth(nLYear, i);
+ }
+
+ //Get Gregorian First of year
+ int nJan1Month = GetYearInfo(nLYear, Jan1Month);
+ int nJan1Date = GetYearInfo(nLYear, Jan1Date);
+
+ // calc the solar day of year of 1 Lunar day
+ int fLeap = GregorianIsLeapYear(nLYear);
+ int[] days = (fLeap == 1) ? DaysToMonth366 : DaysToMonth365;
+
+ nSolarDay = nJan1Date;
+
+ if (nJan1Month > 1)
+ nSolarDay += days[nJan1Month - 1];
+
+ // Add the actual lunar day to get the solar day we want
+ nSolarDay = nSolarDay + numLunarDays;// - 1;
+
+ if (nSolarDay > (fLeap + 365))
+ {
+ nSolarYear = nLYear + 1;
+ nSolarDay -= (fLeap + 365);
+ }
+ else
+ {
+ nSolarYear = nLYear;
+ }
+
+ for (nSolarMonth = 1; nSolarMonth < 12; nSolarMonth++)
+ {
+ if (days[nSolarMonth] >= nSolarDay)
+ break;
+ }
+
+ nSolarDay -= days[nSolarMonth - 1];
+ return true;
+ }
+
+ internal DateTime LunarToTime(DateTime time, int year, int month, int day)
+ {
+ int gy = 0; int gm = 0; int gd = 0;
+ LunarToGregorian(year, month, day, ref gy, ref gm, ref gd);
+ return (GregorianCalendar.GetDefaultInstance().ToDateTime(gy, gm, gd, time.Hour, time.Minute, time.Second, time.Millisecond));
+ }
+
+ internal void TimeToLunar(DateTime time, ref int year, ref int month, ref int day)
+ {
+ int gy = 0; int gm = 0; int gd = 0;
+
+ Calendar Greg = GregorianCalendar.GetDefaultInstance();
+ gy = Greg.GetYear(time);
+ gm = Greg.GetMonth(time);
+ gd = Greg.GetDayOfMonth(time);
+
+ GregorianToLunar(gy, gm, gd, ref year, ref month, ref day);
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // months to the specified DateTime. The result is computed by incrementing
+ // (or decrementing) the year and month parts of the specified DateTime by
+ // value months, and, if required, adjusting the day part of the
+ // resulting date downwards to the last day of the resulting month in the
+ // resulting year. The time-of-day part of the result is the same as the
+ // time-of-day part of the specified DateTime.
+ //
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ if (months < -120000 || months > 120000)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(months),
+ SR.Format(SR.ArgumentOutOfRange_Range, -120000, 120000));
+ }
+ Contract.EndContractBlock();
+
+ CheckTicksRange(time.Ticks);
+
+ int y = 0; int m = 0; int d = 0;
+ TimeToLunar(time, ref y, ref m, ref d);
+
+ int i = m + months;
+ if (i > 0)
+ {
+ int monthsInYear = InternalIsLeapYear(y) ? 13 : 12;
+
+ while (i - monthsInYear > 0)
+ {
+ i -= monthsInYear;
+ y++;
+ monthsInYear = InternalIsLeapYear(y) ? 13 : 12;
+ }
+ m = i;
+ }
+ else
+ {
+ int monthsInYear;
+ while (i <= 0)
+ {
+ monthsInYear = InternalIsLeapYear(y - 1) ? 13 : 12;
+ i += monthsInYear;
+ y--;
+ }
+ m = i;
+ }
+
+ int days = InternalGetDaysInMonth(y, m);
+ if (d > days)
+ {
+ d = days;
+ }
+ DateTime dt = LunarToTime(time, y, m, d);
+
+ CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime);
+ return (dt);
+ }
+
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ CheckTicksRange(time.Ticks);
+
+ int y = 0; int m = 0; int d = 0;
+ TimeToLunar(time, ref y, ref m, ref d);
+
+ y += years;
+
+ if (m == 13 && !InternalIsLeapYear(y))
+ {
+ m = 12;
+ d = InternalGetDaysInMonth(y, m);
+ }
+ int DaysInMonths = InternalGetDaysInMonth(y, m);
+ if (d > DaysInMonths)
+ {
+ d = DaysInMonths;
+ }
+
+ DateTime dt = LunarToTime(time, y, m, d);
+ CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime);
+ return (dt);
+ }
+
+ // Returns the day-of-year part of the specified DateTime. The returned value
+ // is an integer between 1 and [354|355 |383|384].
+ //
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+
+ int y = 0; int m = 0; int d = 0;
+ TimeToLunar(time, ref y, ref m, ref d);
+
+ for (int i = 1; i < m; i++)
+ {
+ d = d + InternalGetDaysInMonth(y, i);
+ }
+ return d;
+ }
+
+ // Returns the day-of-month part of the specified DateTime. The returned
+ // value is an integer between 1 and 29 or 30.
+ //
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+
+ int y = 0; int m = 0; int d = 0;
+ TimeToLunar(time, ref y, ref m, ref d);
+
+ return d;
+ }
+
+ // Returns the number of days in the year given by the year argument for the current era.
+ //
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ year = CheckYearRange(year, era);
+
+ int Days = 0;
+ int monthsInYear = InternalIsLeapYear(year) ? 13 : 12;
+
+ while (monthsInYear != 0)
+ Days += InternalGetDaysInMonth(year, monthsInYear--);
+
+ return Days;
+ }
+
+ // Returns the month part of the specified DateTime. The returned value is an
+ // integer between 1 and 13.
+ //
+
+ public override int GetMonth(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+
+ int y = 0; int m = 0; int d = 0;
+ TimeToLunar(time, ref y, ref m, ref d);
+
+ return m;
+ }
+
+ // Returns the year part of the specified DateTime. The returned value is an
+ // integer between 1 and MaxCalendarYear.
+ //
+
+ public override int GetYear(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+
+ int y = 0; int m = 0; int d = 0;
+ TimeToLunar(time, ref y, ref m, ref d);
+
+ return GetYear(y, time);
+ }
+
+ // Returns the day-of-week part of the specified DateTime. The returned value
+ // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
+ // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
+ // Thursday, 5 indicates Friday, and 6 indicates Saturday.
+ //
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+ return ((DayOfWeek)((int)(time.Ticks / Calendar.TicksPerDay + 1) % 7));
+ }
+
+ // Returns the number of months in the specified year and era.
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ year = CheckYearRange(year, era);
+ return (InternalIsLeapYear(year) ? 13 : 12);
+ }
+
+ // Checks whether a given day in the specified era is a leap day. This method returns true if
+ // the date is a leap day, or false if not.
+ //
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ year = CheckYearMonthRange(year, month, era);
+ int daysInMonth = InternalGetDaysInMonth(year, month);
+
+ if (day < 1 || day > daysInMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ SR.Format(SR.ArgumentOutOfRange_Day, daysInMonth, month));
+ }
+ int m = GetYearInfo(year, LeapMonth);
+ return ((m != 0) && (month == (m + 1)));
+ }
+
+ // Checks whether a given month in the specified era is a leap month. This method returns true if
+ // month is a leap month, or false if not.
+ //
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ year = CheckYearMonthRange(year, month, era);
+ int m = GetYearInfo(year, LeapMonth);
+ return ((m != 0) && (month == (m + 1)));
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this this year is not a leap year.
+ //
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ year = CheckYearRange(year, era);
+ int month = GetYearInfo(year, LeapMonth);
+ if (month > 0)
+ {
+ return (month + 1);
+ }
+ return 0;
+ }
+
+ internal bool InternalIsLeapYear(int year)
+ {
+ return (GetYearInfo(year, LeapMonth) != 0);
+ }
+ // Checks whether a given year in the specified era is a leap year. This method returns true if
+ // year is a leap year, or false if not.
+ //
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ year = CheckYearRange(year, era);
+ return InternalIsLeapYear(year);
+ }
+
+ private const int DEFAULT_GREGORIAN_TWO_DIGIT_YEAR_MAX = 2029;
+
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ if (twoDigitYearMax == -1)
+ {
+ twoDigitYearMax = GetSystemTwoDigitYearSetting(BaseCalendarID, GetYear(new DateTime(DEFAULT_GREGORIAN_TWO_DIGIT_YEAR_MAX, 1, 1)));
+ }
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ VerifyWritable();
+ if (value < 99 || value > MaxCalendarYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(value),
+ SR.Format(SR.ArgumentOutOfRange_Range, 99, MaxCalendarYear));
+ }
+ twoDigitYearMax = value;
+ }
+ }
+
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.EndContractBlock();
+
+ year = base.ToFourDigitYear(year);
+ CheckYearRange(year, CurrentEra);
+ return (year);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/GregorianCalendarTypes.cs b/src/mscorlib/shared/System/Globalization/GregorianCalendarTypes.cs
new file mode 100644
index 0000000000..1b61e5256e
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/GregorianCalendarTypes.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ // Note: The values of the members of this enum must match the coresponding values
+ // in the CalendarId enum (since we cast between GregorianCalendarTypes and CalendarId).
+ [Serializable]
+ public enum GregorianCalendarTypes
+ {
+ Localized = CalendarId.GREGORIAN,
+ USEnglish = CalendarId.GREGORIAN_US,
+ MiddleEastFrench = CalendarId.GREGORIAN_ME_FRENCH,
+ Arabic = CalendarId.GREGORIAN_ARABIC,
+ TransliteratedEnglish = CalendarId.GREGORIAN_XLIT_ENGLISH,
+ TransliteratedFrench = CalendarId.GREGORIAN_XLIT_FRENCH,
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs b/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs
new file mode 100644
index 0000000000..b4f54f8fbb
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/HebrewCalendar.cs
@@ -0,0 +1,1129 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Rules for the Hebrew calendar:
+ // - The Hebrew calendar is both a Lunar (months) and Solar (years)
+ // calendar, but allows for a week of seven days.
+ // - Days begin at sunset.
+ // - Leap Years occur in the 3, 6, 8, 11, 14, 17, & 19th years of a
+ // 19-year cycle. Year = leap iff ((7y+1) mod 19 < 7).
+ // - There are 12 months in a common year and 13 months in a leap year.
+ // - In a common year, the 6th month, Adar, has 29 days. In a leap
+ // year, the 6th month, Adar I, has 30 days and the leap month,
+ // Adar II, has 29 days.
+ // - Common years have 353-355 days. Leap years have 383-385 days.
+ // - The Hebrew new year (Rosh HaShanah) begins on the 1st of Tishri,
+ // the 7th month in the list below.
+ // - The new year may not begin on Sunday, Wednesday, or Friday.
+ // - If the new year would fall on a Tuesday and the conjunction of
+ // the following year were at midday or later, the new year is
+ // delayed until Thursday.
+ // - If the new year would fall on a Monday after a leap year, the
+ // new year is delayed until Tuesday.
+ // - The length of the 8th and 9th months vary from year to year,
+ // depending on the overall length of the year.
+ // - The length of a year is determined by the dates of the new
+ // years (Tishri 1) preceding and following the year in question.
+ // - The 2th month is long (30 days) if the year has 355 or 385 days.
+ // - The 3th month is short (29 days) if the year has 353 or 383 days.
+ // - The Hebrew months are:
+ // 1. Tishri (30 days)
+ // 2. Heshvan (29 or 30 days)
+ // 3. Kislev (29 or 30 days)
+ // 4. Teveth (29 days)
+ // 5. Shevat (30 days)
+ // 6. Adar I (30 days)
+ // 7. Adar {II} (29 days, this only exists if that year is a leap year)
+ // 8. Nisan (30 days)
+ // 9. Iyyar (29 days)
+ // 10. Sivan (30 days)
+ // 11. Tammuz (29 days)
+ // 12. Av (30 days)
+ // 13. Elul (29 days)
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /*
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 1583/01/01 2239/09/29
+ ** Hebrew 5343/04/07 5999/13/29
+ */
+
+ // Includes CHebrew implemetation;i.e All the code necessary for converting
+ // Gregorian to Hebrew Lunar from 1583 to 2239.
+
+
+ [Serializable]
+ public class HebrewCalendar : Calendar
+ {
+ public static readonly int HebrewEra = 1;
+
+ internal const int DatePartYear = 0;
+ internal const int DatePartDayOfYear = 1;
+ internal const int DatePartMonth = 2;
+ internal const int DatePartDay = 3;
+ internal const int DatePartDayOfWeek = 4;
+
+ //
+ // Hebrew Translation Table.
+ //
+ // This table is used to get the following Hebrew calendar information for a
+ // given Gregorian year:
+ // 1. The day of the Hebrew month corresponding to Gregorian January 1st
+ // for a given Gregorian year.
+ // 2. The month of the Hebrew month corresponding to Gregorian January 1st
+ // for a given Gregorian year.
+ // The information is not directly in the table. Instead, the info is decoded
+ // by special values (numbers above 29 and below 1).
+ // 3. The type of the Hebrew year for a given Gregorian year.
+ //
+
+ /*
+ More notes:
+
+ This table includes 2 numbers for each year.
+ The offset into the table determines the year. (offset 0 is Gregorian year 1500)
+ 1st number determines the day of the Hebrew month coresponeds to January 1st.
+ 2nd number determines the type of the Hebrew year. (the type determines how
+ many days are there in the year.)
+
+ normal years : 1 = 353 days 2 = 354 days 3 = 355 days.
+ Leap years : 4 = 383 5 384 6 = 385 days.
+
+ A 99 means the year is not supported for translation.
+ for convenience the table was defined for 750 year,
+ but only 640 years are supported. (from 1583 to 2239)
+ the years before 1582 (starting of Georgian calander)
+ and after 2239, are filled with 99.
+
+ Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days.
+ That's why, there no nead to specify the lunar month in the table.
+ There are exceptions, these are coded by giving numbers above 29 and below 1.
+ Actual decoding is takenig place whenever fetching information from the table.
+ The function for decoding is in GetLunarMonthDay().
+
+ Example:
+ The data for 2000 - 2005 A.D. is:
+
+ 23,6,6,1,17,2,27,6,7,3, // 2000 - 2004
+
+ For year 2000, we know it has a Hebrew year type 6, which means it has 385 days.
+ And 1/1/2000 A.D. is Hebrew year 5760, 23rd day of 4th month.
+ */
+
+ //
+ // Jewish Era in use today is dated from the supposed year of the
+ // Creation with its beginning in 3761 B.C.
+ //
+
+ // The Hebrew year of Gregorian 1st year AD.
+ // 0001/01/01 AD is Hebrew 3760/01/01
+ private const int HebrewYearOf1AD = 3760;
+
+ // The first Gregorian year in HebrewTable.
+ private const int FirstGregorianTableYear = 1583; // == Hebrew Year 5343
+ // The last Gregorian year in HebrewTable.
+ private const int LastGregorianTableYear = 2239; // == Hebrew Year 5999
+ private const int TABLESIZE = (LastGregorianTableYear - FirstGregorianTableYear);
+
+ private const int MinHebrewYear = HebrewYearOf1AD + FirstGregorianTableYear; // == 5343
+ private const int MaxHebrewYear = HebrewYearOf1AD + LastGregorianTableYear; // == 5999
+
+ private static readonly byte[] s_hebrewTable = {
+ 7,3,17,3, // 1583-1584 (Hebrew year: 5343 - 5344)
+ 0,4,11,2,21,6,1,3,13,2, // 1585-1589
+ 25,4,5,3,16,2,27,6,9,1, // 1590-1594
+ 20,2,0,6,11,3,23,4,4,2, // 1595-1599
+ 14,3,27,4,8,2,18,3,28,6, // 1600
+ 11,1,22,5,2,3,12,3,25,4, // 1605
+ 6,2,16,3,26,6,8,2,20,1, // 1610
+ 0,6,11,2,24,4,4,3,15,2, // 1615
+ 25,6,8,1,19,2,29,6,9,3, // 1620
+ 22,4,3,2,13,3,25,4,6,3, // 1625
+ 17,2,27,6,7,3,19,2,31,4, // 1630
+ 11,3,23,4,5,2,15,3,25,6, // 1635
+ 6,2,19,1,29,6,10,2,22,4, // 1640
+ 3,3,14,2,24,6,6,1,17,3, // 1645
+ 28,5,8,3,20,1,32,5,12,3, // 1650
+ 22,6,4,1,16,2,26,6,6,3, // 1655
+ 17,2,0,4,10,3,22,4,3,2, // 1660
+ 14,3,24,6,5,2,17,1,28,6, // 1665
+ 9,2,19,3,31,4,13,2,23,6, // 1670
+ 3,3,15,1,27,5,7,3,17,3, // 1675
+ 29,4,11,2,21,6,3,1,14,2, // 1680
+ 25,6,5,3,16,2,28,4,9,3, // 1685
+ 20,2,0,6,12,1,23,6,4,2, // 1690
+ 14,3,26,4,8,2,18,3,0,4, // 1695
+ 10,3,21,5,1,3,13,1,24,5, // 1700
+ 5,3,15,3,27,4,8,2,19,3, // 1705
+ 29,6,10,2,22,4,3,3,14,2, // 1710
+ 26,4,6,3,18,2,28,6,10,1, // 1715
+ 20,6,2,2,12,3,24,4,5,2, // 1720
+ 16,3,28,4,8,3,19,2,0,6, // 1725
+ 12,1,23,5,3,3,14,3,26,4, // 1730
+ 7,2,17,3,28,6,9,2,21,4, // 1735
+ 1,3,13,2,25,4,5,3,16,2, // 1740
+ 27,6,9,1,19,3,0,5,11,3, // 1745
+ 23,4,4,2,14,3,25,6,7,1, // 1750
+ 18,2,28,6,9,3,21,4,2,2, // 1755
+ 12,3,25,4,6,2,16,3,26,6, // 1760
+ 8,2,20,1,0,6,11,2,22,6, // 1765
+ 4,1,15,2,25,6,6,3,18,1, // 1770
+ 29,5,9,3,22,4,2,3,13,2, // 1775
+ 23,6,4,3,15,2,27,4,7,3, // 1780
+ 19,2,31,4,11,3,21,6,3,2, // 1785
+ 15,1,25,6,6,2,17,3,29,4, // 1790
+ 10,2,20,6,3,1,13,3,24,5, // 1795
+ 4,3,16,1,27,5,7,3,17,3, // 1800
+ 0,4,11,2,21,6,1,3,13,2, // 1805
+ 25,4,5,3,16,2,29,4,9,3, // 1810
+ 19,6,30,2,13,1,23,6,4,2, // 1815
+ 14,3,27,4,8,2,18,3,0,4, // 1820
+ 11,3,22,5,2,3,14,1,26,5, // 1825
+ 6,3,16,3,28,4,10,2,20,6, // 1830
+ 30,3,11,2,24,4,4,3,15,2, // 1835
+ 25,6,8,1,19,2,29,6,9,3, // 1840
+ 22,4,3,2,13,3,25,4,7,2, // 1845
+ 17,3,27,6,9,1,21,5,1,3, // 1850
+ 11,3,23,4,5,2,15,3,25,6, // 1855
+ 6,2,19,1,29,6,10,2,22,4, // 1860
+ 3,3,14,2,24,6,6,1,18,2, // 1865
+ 28,6,8,3,20,4,2,2,12,3, // 1870
+ 24,4,4,3,16,2,26,6,6,3, // 1875
+ 17,2,0,4,10,3,22,4,3,2, // 1880
+ 14,3,24,6,5,2,17,1,28,6, // 1885
+ 9,2,21,4,1,3,13,2,23,6, // 1890
+ 5,1,15,3,27,5,7,3,19,1, // 1895
+ 0,5,10,3,22,4,2,3,13,2, // 1900
+ 24,6,4,3,15,2,27,4,8,3, // 1905
+ 20,4,1,2,11,3,22,6,3,2, // 1910
+ 15,1,25,6,7,2,17,3,29,4, // 1915
+ 10,2,21,6,1,3,13,1,24,5, // 1920
+ 5,3,15,3,27,4,8,2,19,6, // 1925
+ 1,1,12,2,22,6,3,3,14,2, // 1930
+ 26,4,6,3,18,2,28,6,10,1, // 1935
+ 20,6,2,2,12,3,24,4,5,2, // 1940
+ 16,3,28,4,9,2,19,6,30,3, // 1945
+ 12,1,23,5,3,3,14,3,26,4, // 1950
+ 7,2,17,3,28,6,9,2,21,4, // 1955
+ 1,3,13,2,25,4,5,3,16,2, // 1960
+ 27,6,9,1,19,6,30,2,11,3, // 1965
+ 23,4,4,2,14,3,27,4,7,3, // 1970
+ 18,2,28,6,11,1,22,5,2,3, // 1975
+ 12,3,25,4,6,2,16,3,26,6, // 1980
+ 8,2,20,4,30,3,11,2,24,4, // 1985
+ 4,3,15,2,25,6,8,1,18,3, // 1990
+ 29,5,9,3,22,4,3,2,13,3, // 1995
+ 23,6,6,1,17,2,27,6,7,3, // 2000 - 2004
+ 20,4,1,2,11,3,23,4,5,2, // 2005 - 2009
+ 15,3,25,6,6,2,19,1,29,6, // 2010
+ 10,2,20,6,3,1,14,2,24,6, // 2015
+ 4,3,17,1,28,5,8,3,20,4, // 2020
+ 1,3,12,2,22,6,2,3,14,2, // 2025
+ 26,4,6,3,17,2,0,4,10,3, // 2030
+ 20,6,1,2,14,1,24,6,5,2, // 2035
+ 15,3,28,4,9,2,19,6,1,1, // 2040
+ 12,3,23,5,3,3,15,1,27,5, // 2045
+ 7,3,17,3,29,4,11,2,21,6, // 2050
+ 1,3,12,2,25,4,5,3,16,2, // 2055
+ 28,4,9,3,19,6,30,2,12,1, // 2060
+ 23,6,4,2,14,3,26,4,8,2, // 2065
+ 18,3,0,4,10,3,22,5,2,3, // 2070
+ 14,1,25,5,6,3,16,3,28,4, // 2075
+ 9,2,20,6,30,3,11,2,23,4, // 2080
+ 4,3,15,2,27,4,7,3,19,2, // 2085
+ 29,6,11,1,21,6,3,2,13,3, // 2090
+ 25,4,6,2,17,3,27,6,9,1, // 2095
+ 20,5,30,3,10,3,22,4,3,2, // 2100
+ 14,3,24,6,5,2,17,1,28,6, // 2105
+ 9,2,21,4,1,3,13,2,23,6, // 2110
+ 5,1,16,2,27,6,7,3,19,4, // 2115
+ 30,2,11,3,23,4,3,3,14,2, // 2120
+ 25,6,5,3,16,2,28,4,9,3, // 2125
+ 21,4,2,2,12,3,23,6,4,2, // 2130
+ 16,1,26,6,8,2,20,4,30,3, // 2135
+ 11,2,22,6,4,1,14,3,25,5, // 2140
+ 6,3,18,1,29,5,9,3,22,4, // 2145
+ 2,3,13,2,23,6,4,3,15,2, // 2150
+ 27,4,7,3,20,4,1,2,11,3, // 2155
+ 21,6,3,2,15,1,25,6,6,2, // 2160
+ 17,3,29,4,10,2,20,6,3,1, // 2165
+ 13,3,24,5,4,3,17,1,28,5, // 2170
+ 8,3,18,6,1,1,12,2,22,6, // 2175
+ 2,3,14,2,26,4,6,3,17,2, // 2180
+ 28,6,10,1,20,6,1,2,12,3, // 2185
+ 24,4,5,2,15,3,28,4,9,2, // 2190
+ 19,6,33,3,12,1,23,5,3,3, // 2195
+ 13,3,25,4,6,2,16,3,26,6, // 2200
+ 8,2,20,4,30,3,11,2,24,4, // 2205
+ 4,3,15,2,25,6,8,1,18,6, // 2210
+ 33,2,9,3,22,4,3,2,13,3, // 2215
+ 25,4,6,3,17,2,27,6,9,1, // 2220
+ 21,5,1,3,11,3,23,4,5,2, // 2225
+ 15,3,25,6,6,2,19,4,33,3, // 2230
+ 10,2,22,4,3,3,14,2,24,6, // 2235
+ 6,1 // 2240 (Hebrew year: 6000)
+ };
+
+ private const int MaxMonthPlusOne = 14;
+
+ //
+ // The lunar calendar has 6 different variations of month lengths
+ // within a year.
+ //
+ private static readonly byte[] s_lunarMonthLen = {
+ 0,00,00,00,00,00,00,00,00,00,00,00,00,0,
+ 0,30,29,29,29,30,29,30,29,30,29,30,29,0, // 3 common year variations
+ 0,30,29,30,29,30,29,30,29,30,29,30,29,0,
+ 0,30,30,30,29,30,29,30,29,30,29,30,29,0,
+ 0,30,29,29,29,30,30,29,30,29,30,29,30,29, // 3 leap year variations
+ 0,30,29,30,29,30,30,29,30,29,30,29,30,29,
+ 0,30,30,30,29,30,30,29,30,29,30,29,30,29
+ };
+
+ internal static readonly DateTime calendarMinValue = new DateTime(1583, 1, 1);
+ // Gregorian 2239/9/29 = Hebrew 5999/13/29 (last day in Hebrew year 5999).
+ // We can only format/parse Hebrew numbers up to 999, so we limit the max range to Hebrew year 5999.
+ internal static readonly DateTime calendarMaxValue = new DateTime((new DateTime(2239, 9, 29, 23, 59, 59, 999)).Ticks + 9999);
+
+
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (calendarMinValue);
+ }
+ }
+
+
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (calendarMaxValue);
+ }
+ }
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.LunisolarCalendar;
+ }
+ }
+
+ public HebrewCalendar()
+ {
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return (CalendarId.HEBREW);
+ }
+ }
+
+
+ /*=================================CheckHebrewYearValue==========================
+ **Action: Check if the Hebrew year value is supported in this class.
+ **Returns: None.
+ **Arguments: y Hebrew year value
+ ** ear Hebrew era value
+ **Exceptions: ArgumentOutOfRange_Range if the year value is not supported.
+ **Note:
+ ** We use a table for the Hebrew calendar calculation, so the year supported is limited.
+ ============================================================================*/
+
+ private static void CheckHebrewYearValue(int y, int era, String varName)
+ {
+ CheckEraRange(era);
+ if (y > MaxHebrewYear || y < MinHebrewYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ varName,
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ MinHebrewYear,
+ MaxHebrewYear));
+ }
+ }
+
+ /*=================================CheckHebrewMonthValue==========================
+ **Action: Check if the Hebrew month value is valid.
+ **Returns: None.
+ **Arguments: year Hebrew year value
+ ** month Hebrew month value
+ **Exceptions: ArgumentOutOfRange_Range if the month value is not valid.
+ **Note:
+ ** Call CheckHebrewYearValue() before calling this to verify the year value is supported.
+ ============================================================================*/
+
+ private void CheckHebrewMonthValue(int year, int month, int era)
+ {
+ int monthsInYear = GetMonthsInYear(year, era);
+ if (month < 1 || month > monthsInYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(month),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ monthsInYear));
+ }
+ }
+
+ /*=================================CheckHebrewDayValue==========================
+ **Action: Check if the Hebrew day value is valid.
+ **Returns: None.
+ **Arguments: year Hebrew year value
+ ** month Hebrew month value
+ ** day Hebrew day value.
+ **Exceptions: ArgumentOutOfRange_Range if the day value is not valid.
+ **Note:
+ ** Call CheckHebrewYearValue()/CheckHebrewMonthValue() before calling this to verify the year/month values are valid.
+ ============================================================================*/
+
+ private void CheckHebrewDayValue(int year, int month, int day, int era)
+ {
+ int daysInMonth = GetDaysInMonth(year, month, era);
+ if (day < 1 || day > daysInMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ daysInMonth));
+ }
+ }
+
+ internal static void CheckEraRange(int era)
+ {
+ if (era != CurrentEra && era != HebrewEra)
+ {
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+ }
+
+ private static void CheckTicksRange(long ticks)
+ {
+ if (ticks < calendarMinValue.Ticks || ticks > calendarMaxValue.Ticks)
+ {
+ throw new ArgumentOutOfRangeException(
+ "time",
+ // Print out the date in Gregorian using InvariantCulture since the DateTime is based on GreograinCalendar.
+ String.Format(
+ CultureInfo.InvariantCulture,
+ SR.ArgumentOutOfRange_CalendarRange,
+ calendarMinValue,
+ calendarMaxValue));
+ }
+ }
+
+ internal static int GetResult(__DateBuffer result, int part)
+ {
+ switch (part)
+ {
+ case DatePartYear:
+ return (result.year);
+ case DatePartMonth:
+ return (result.month);
+ case DatePartDay:
+ return (result.day);
+ }
+
+ throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing);
+ }
+
+ /*=================================GetLunarMonthDay==========================
+ **Action: Using the Hebrew table (HebrewTable) to get the Hebrew month/day value for Gregorian January 1st
+ ** in a given Gregorian year.
+ ** Greogrian January 1st falls usually in Tevet (4th month). Tevet has always 29 days.
+ ** That's why, there no nead to specify the lunar month in the table. There are exceptions, and these
+ ** are coded by giving numbers above 29 and below 1.
+ ** Actual decoding is takenig place in the switch statement below.
+ **Returns:
+ ** The Hebrew year type. The value is from 1 to 6.
+ ** normal years : 1 = 353 days 2 = 354 days 3 = 355 days.
+ ** Leap years : 4 = 383 5 384 6 = 385 days.
+ **Arguments:
+ ** gregorianYear The year value in Gregorian calendar. The value should be between 1500 and 2239.
+ ** lunarDate Object to take the result of the Hebrew year/month/day.
+ **Exceptions:
+ ============================================================================*/
+
+ internal static int GetLunarMonthDay(int gregorianYear, __DateBuffer lunarDate)
+ {
+ //
+ // Get the offset into the LunarMonthLen array and the lunar day
+ // for January 1st.
+ //
+ int index = gregorianYear - FirstGregorianTableYear;
+ if (index < 0 || index > TABLESIZE)
+ {
+ throw new ArgumentOutOfRangeException(nameof(gregorianYear));
+ }
+
+ index *= 2;
+ lunarDate.day = s_hebrewTable[index];
+
+ // Get the type of the year. The value is from 1 to 6
+ int LunarYearType = s_hebrewTable[index + 1];
+
+ //
+ // Get the Lunar Month.
+ //
+ switch (lunarDate.day)
+ {
+ case (0): // 1/1 is on Shvat 1
+ lunarDate.month = 5;
+ lunarDate.day = 1;
+ break;
+ case (30): // 1/1 is on Kislev 30
+ lunarDate.month = 3;
+ break;
+ case (31): // 1/1 is on Shvat 2
+ lunarDate.month = 5;
+ lunarDate.day = 2;
+ break;
+ case (32): // 1/1 is on Shvat 3
+ lunarDate.month = 5;
+ lunarDate.day = 3;
+ break;
+ case (33): // 1/1 is on Kislev 29
+ lunarDate.month = 3;
+ lunarDate.day = 29;
+ break;
+ default: // 1/1 is on Tevet (This is the general case)
+ lunarDate.month = 4;
+ break;
+ }
+ return (LunarYearType);
+ }
+
+ // Returns a given date part of this DateTime. This method is used
+ // to compute the year, day-of-year, month, or day part.
+
+ internal virtual int GetDatePart(long ticks, int part)
+ {
+ // The Gregorian year, month, day value for ticks.
+ int gregorianYear, gregorianMonth, gregorianDay;
+ int hebrewYearType; // lunar year type
+ long AbsoluteDate; // absolute date - absolute date 1/1/1600
+
+ //
+ // Make sure we have a valid Gregorian date that will fit into our
+ // Hebrew conversion limits.
+ //
+ CheckTicksRange(ticks);
+
+ DateTime time = new DateTime(ticks);
+
+ //
+ // Save the Gregorian date values.
+ //
+ gregorianYear = time.Year;
+ gregorianMonth = time.Month;
+ gregorianDay = time.Day;
+
+ __DateBuffer lunarDate = new __DateBuffer(); // lunar month and day for Jan 1
+
+ // From the table looking-up value of HebrewTable[index] (stored in lunarDate.day), we get the the
+ // lunar month and lunar day where the Gregorian date 1/1 falls.
+ lunarDate.year = gregorianYear + HebrewYearOf1AD;
+ hebrewYearType = GetLunarMonthDay(gregorianYear, lunarDate);
+
+ // This is the buffer used to store the result Hebrew date.
+ __DateBuffer result = new __DateBuffer();
+
+ //
+ // Store the values for the start of the new year - 1/1.
+ //
+ result.year = lunarDate.year;
+ result.month = lunarDate.month;
+ result.day = lunarDate.day;
+
+ //
+ // Get the absolute date from 1/1/1600.
+ //
+ AbsoluteDate = GregorianCalendar.GetAbsoluteDate(gregorianYear, gregorianMonth, gregorianDay);
+
+ //
+ // If the requested date was 1/1, then we're done.
+ //
+ if ((gregorianMonth == 1) && (gregorianDay == 1))
+ {
+ return (GetResult(result, part));
+ }
+
+ //
+ // Calculate the number of days between 1/1 and the requested date.
+ //
+ long NumDays; // number of days since 1/1
+ NumDays = AbsoluteDate - GregorianCalendar.GetAbsoluteDate(gregorianYear, 1, 1);
+
+ //
+ // If the requested date is within the current lunar month, then
+ // we're done.
+ //
+ if ((NumDays + (long)lunarDate.day) <= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.month]))
+ {
+ result.day += (int)NumDays;
+ return (GetResult(result, part));
+ }
+
+ //
+ // Adjust for the current partial month.
+ //
+ result.month++;
+ result.day = 1;
+
+ //
+ // Adjust the Lunar Month and Year (if necessary) based on the number
+ // of days between 1/1 and the requested date.
+ //
+ // Assumes Jan 1 can never translate to the last Lunar month, which
+ // is true.
+ //
+ NumDays -= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + lunarDate.month] - lunarDate.day);
+ Debug.Assert(NumDays >= 1, "NumDays >= 1");
+
+ // If NumDays is 1, then we are done. Otherwise, find the correct Hebrew month
+ // and day.
+ if (NumDays > 1)
+ {
+ //
+ // See if we're on the correct Lunar month.
+ //
+ while (NumDays > (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month]))
+ {
+ //
+ // Adjust the number of days and move to the next month.
+ //
+ NumDays -= (long)(s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month++]);
+
+ //
+ // See if we need to adjust the Year.
+ // Must handle both 12 and 13 month years.
+ //
+ if ((result.month > 13) || (s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + result.month] == 0))
+ {
+ //
+ // Adjust the Year.
+ //
+ result.year++;
+ hebrewYearType = s_hebrewTable[(gregorianYear + 1 - FirstGregorianTableYear) * 2 + 1];
+
+ //
+ // Adjust the Month.
+ //
+ result.month = 1;
+ }
+ }
+ //
+ // Found the right Lunar month.
+ //
+ result.day += (int)(NumDays - 1);
+ }
+ return (GetResult(result, part));
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // months to the specified DateTime. The result is computed by incrementing
+ // (or decrementing) the year and month parts of the specified DateTime by
+ // value months, and, if required, adjusting the day part of the
+ // resulting date downwards to the last day of the resulting month in the
+ // resulting year. The time-of-day part of the result is the same as the
+ // time-of-day part of the specified DateTime.
+ //
+ // In more precise terms, considering the specified DateTime to be of the
+ // form y / m / d + t, where y is the
+ // year, m is the month, d is the day, and t is the
+ // time-of-day, the result is y1 / m1 / d1 + t,
+ // where y1 and m1 are computed by adding value months
+ // to y and m, and d1 is the largest value less than
+ // or equal to d that denotes a valid day in month m1 of year
+ // y1.
+ //
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ try
+ {
+ int y = GetDatePart(time.Ticks, DatePartYear);
+ int m = GetDatePart(time.Ticks, DatePartMonth);
+ int d = GetDatePart(time.Ticks, DatePartDay);
+
+
+ int monthsInYear;
+ int i;
+ if (months >= 0)
+ {
+ i = m + months;
+ while (i > (monthsInYear = GetMonthsInYear(y, CurrentEra)))
+ {
+ y++;
+ i -= monthsInYear;
+ }
+ }
+ else
+ {
+ if ((i = m + months) <= 0)
+ {
+ months = -months;
+ months -= m;
+ y--;
+
+ while (months > (monthsInYear = GetMonthsInYear(y, CurrentEra)))
+ {
+ y--;
+ months -= monthsInYear;
+ }
+ monthsInYear = GetMonthsInYear(y, CurrentEra);
+ i = monthsInYear - months;
+ }
+ }
+
+ int days = GetDaysInMonth(y, i);
+ if (d > days)
+ {
+ d = days;
+ }
+ return (new DateTime(ToDateTime(y, i, d, 0, 0, 0, 0).Ticks + (time.Ticks % TicksPerDay)));
+ }
+ // We expect ArgumentException and ArgumentOutOfRangeException (which is subclass of ArgumentException)
+ // If exception is thrown in the calls above, we are out of the supported range of this calendar.
+ catch (ArgumentException)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(months),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_AddValue));
+ }
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // years to the specified DateTime. The result is computed by incrementing
+ // (or decrementing) the year part of the specified DateTime by value
+ // years. If the month and day of the specified DateTime is 2/29, and if the
+ // resulting year is not a leap year, the month and day of the resulting
+ // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
+ // parts of the result are the same as those of the specified DateTime.
+ //
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ int y = GetDatePart(time.Ticks, DatePartYear);
+ int m = GetDatePart(time.Ticks, DatePartMonth);
+ int d = GetDatePart(time.Ticks, DatePartDay);
+
+ y += years;
+ CheckHebrewYearValue(y, Calendar.CurrentEra, nameof(years));
+
+ int months = GetMonthsInYear(y, CurrentEra);
+ if (m > months)
+ {
+ m = months;
+ }
+
+ int days = GetDaysInMonth(y, m);
+ if (d > days)
+ {
+ d = days;
+ }
+
+ long ticks = ToDateTime(y, m, d, 0, 0, 0, 0).Ticks + (time.Ticks % TicksPerDay);
+ Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
+ return (new DateTime(ticks));
+ }
+
+ // Returns the day-of-month part of the specified DateTime. The returned
+ // value is an integer between 1 and 31.
+ //
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartDay));
+ }
+
+ // Returns the day-of-week part of the specified DateTime. The returned value
+ // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
+ // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
+ // Thursday, 5 indicates Friday, and 6 indicates Saturday.
+ //
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ // If we calculate back, the Hebrew day of week for Gregorian 0001/1/1 is Monday (1).
+ // Therfore, the fomula is:
+ return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
+ }
+
+ internal static int GetHebrewYearType(int year, int era)
+ {
+ CheckHebrewYearValue(year, era, nameof(year));
+ // The HebrewTable is indexed by Gregorian year and starts from FirstGregorianYear.
+ // So we need to convert year (Hebrew year value) to Gregorian Year below.
+ return (s_hebrewTable[(year - HebrewYearOf1AD - FirstGregorianTableYear) * 2 + 1]);
+ }
+
+ // Returns the day-of-year part of the specified DateTime. The returned value
+ // is an integer between 1 and 366.
+ //
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ // Get Hebrew year value of the specified time.
+ int year = GetYear(time);
+ DateTime beginOfYearDate;
+ if (year == 5343)
+ {
+ // Gregorian 1583/01/01 corresponds to Hebrew 5343/04/07 (MinSupportedDateTime)
+ // To figure out the Gregorian date associated with Hebrew 5343/01/01, we need to
+ // count the days from 5343/01/01 to 5343/04/07 and subtract that from Gregorian
+ // 1583/01/01.
+ // 1. Tishri (30 days)
+ // 2. Heshvan (30 days since 5343 has 355 days)
+ // 3. Kislev (30 days since 5343 has 355 days)
+ // 96 days to get from 5343/01/01 to 5343/04/07
+ // Gregorian 1583/01/01 - 96 days = 1582/9/27
+
+ // the beginning of Hebrew year 5343 corresponds to Gregorian September 27, 1582.
+ beginOfYearDate = new DateTime(1582, 9, 27);
+ }
+ else
+ {
+ // following line will fail when year is 5343 (first supported year)
+ beginOfYearDate = ToDateTime(year, 1, 1, 0, 0, 0, 0, CurrentEra);
+ }
+ return ((int)((time.Ticks - beginOfYearDate.Ticks) / TicksPerDay) + 1);
+ }
+
+ // Returns the number of days in the month given by the year and
+ // month arguments.
+ //
+
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ CheckEraRange(era);
+ int hebrewYearType = GetHebrewYearType(year, era);
+ CheckHebrewMonthValue(year, month, era);
+
+ Debug.Assert(hebrewYearType >= 1 && hebrewYearType <= 6,
+ "hebrewYearType should be from 1 to 6, but now hebrewYearType = " + hebrewYearType + " for hebrew year " + year);
+ int monthDays = s_lunarMonthLen[hebrewYearType * MaxMonthPlusOne + month];
+ if (monthDays == 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
+ }
+ return (monthDays);
+ }
+
+ // Returns the number of days in the year given by the year argument for the current era.
+ //
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ CheckEraRange(era);
+ // normal years : 1 = 353 days 2 = 354 days 3 = 355 days.
+ // Leap years : 4 = 383 5 384 6 = 385 days.
+
+ // LunarYearType is from 1 to 6
+ int LunarYearType = GetHebrewYearType(year, era);
+ if (LunarYearType < 4)
+ {
+ // common year: LunarYearType = 1, 2, 3
+ return (352 + LunarYearType);
+ }
+ return (382 + (LunarYearType - 3));
+ }
+
+ // Returns the era for the specified DateTime value.
+
+ public override int GetEra(DateTime time)
+ {
+ return (HebrewEra);
+ }
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (new int[] { HebrewEra });
+ }
+ }
+
+ // Returns the month part of the specified DateTime. The returned value is an
+ // integer between 1 and 12.
+ //
+
+ public override int GetMonth(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartMonth));
+ }
+
+ // Returns the number of months in the specified year and era.
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ return (IsLeapYear(year, era) ? 13 : 12);
+ }
+
+ // Returns the year part of the specified DateTime. The returned value is an
+ // integer between 1 and 9999.
+ //
+
+ public override int GetYear(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartYear));
+ }
+
+ // Checks whether a given day in the specified era is a leap day. This method returns true if
+ // the date is a leap day, or false if not.
+ //
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ if (IsLeapMonth(year, month, era))
+ {
+ // Every day in a leap month is a leap day.
+ CheckHebrewDayValue(year, month, day, era);
+ return (true);
+ }
+ else if (IsLeapYear(year, Calendar.CurrentEra))
+ {
+ // There is an additional day in the 6th month in the leap year (the extra day is the 30th day in the 6th month),
+ // so we should return true for 6/30 if that's in a leap year.
+ if (month == 6 && day == 30)
+ {
+ return (true);
+ }
+ }
+ CheckHebrewDayValue(year, month, day, era);
+ return (false);
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this calendar does not have leap month, or this year is not a leap year.
+ //
+
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ // Year/era values are checked in IsLeapYear().
+ if (IsLeapYear(year, era))
+ {
+ // The 7th month in a leap year is a leap month.
+ return (7);
+ }
+ return (0);
+ }
+
+ // Checks whether a given month in the specified era is a leap month. This method returns true if
+ // month is a leap month, or false if not.
+ //
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ // Year/era values are checked in IsLeapYear().
+ bool isLeapYear = IsLeapYear(year, era);
+ CheckHebrewMonthValue(year, month, era);
+ // The 7th month in a leap year is a leap month.
+ if (isLeapYear)
+ {
+ if (month == 7)
+ {
+ return (true);
+ }
+ }
+ return (false);
+ }
+
+ // Checks whether a given year in the specified era is a leap year. This method returns true if
+ // year is a leap year, or false if not.
+ //
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ CheckHebrewYearValue(year, era, nameof(year));
+ return (((7 * (long)year + 1) % 19) < 7);
+ }
+
+ // (month1, day1) - (month2, day2)
+ private static int GetDayDifference(int lunarYearType, int month1, int day1, int month2, int day2)
+ {
+ if (month1 == month2)
+ {
+ return (day1 - day2);
+ }
+
+ // Make sure that (month1, day1) < (month2, day2)
+ bool swap = (month1 > month2);
+ if (swap)
+ {
+ // (month1, day1) < (month2, day2). Swap the values.
+ // The result will be a negative number.
+ int tempMonth, tempDay;
+ tempMonth = month1; tempDay = day1;
+ month1 = month2; day1 = day2;
+ month2 = tempMonth; day2 = tempDay;
+ }
+
+ // Get the number of days from (month1,day1) to (month1, end of month1)
+ int days = s_lunarMonthLen[lunarYearType * MaxMonthPlusOne + month1] - day1;
+
+ // Move to next month.
+ month1++;
+
+ // Add up the days.
+ while (month1 < month2)
+ {
+ days += s_lunarMonthLen[lunarYearType * MaxMonthPlusOne + month1++];
+ }
+ days += day2;
+
+ return (swap ? days : -days);
+ }
+
+ /*=================================HebrewToGregorian==========================
+ **Action: Convert Hebrew date to Gregorian date.
+ **Returns:
+ **Arguments:
+ **Exceptions:
+ ** The algorithm is like this:
+ ** The hebrew year has an offset to the Gregorian year, so we can guess the Gregorian year for
+ ** the specified Hebrew year. That is, GreogrianYear = HebrewYear - FirstHebrewYearOf1AD.
+ **
+ ** From the Gregorian year and HebrewTable, we can get the Hebrew month/day value
+ ** of the Gregorian date January 1st. Let's call this month/day value [hebrewDateForJan1]
+ **
+ ** If the requested Hebrew month/day is less than [hebrewDateForJan1], we know the result
+ ** Gregorian date falls in previous year. So we decrease the Gregorian year value, and
+ ** retrieve the Hebrew month/day value of the Gregorian date january 1st again.
+ **
+ ** Now, we get the answer of the Gregorian year.
+ **
+ ** The next step is to get the number of days between the requested Hebrew month/day
+ ** and [hebrewDateForJan1]. When we get that, we can create the DateTime by adding/subtracting
+ ** the ticks value of the number of days.
+ **
+ ============================================================================*/
+
+
+ private static DateTime HebrewToGregorian(int hebrewYear, int hebrewMonth, int hebrewDay, int hour, int minute, int second, int millisecond)
+ {
+ // Get the rough Gregorian year for the specified hebrewYear.
+ //
+ int gregorianYear = hebrewYear - HebrewYearOf1AD;
+
+ __DateBuffer hebrewDateOfJan1 = new __DateBuffer(); // year value is unused.
+ int lunarYearType = GetLunarMonthDay(gregorianYear, hebrewDateOfJan1);
+
+ if ((hebrewMonth == hebrewDateOfJan1.month) && (hebrewDay == hebrewDateOfJan1.day))
+ {
+ return (new DateTime(gregorianYear, 1, 1, hour, minute, second, millisecond));
+ }
+
+ int days = GetDayDifference(lunarYearType, hebrewMonth, hebrewDay, hebrewDateOfJan1.month, hebrewDateOfJan1.day);
+
+ DateTime gregorianNewYear = new DateTime(gregorianYear, 1, 1);
+ return (new DateTime(gregorianNewYear.Ticks + days * TicksPerDay
+ + TimeToTicks(hour, minute, second, millisecond)));
+ }
+
+ // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
+ //
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ CheckHebrewYearValue(year, era, nameof(year));
+ CheckHebrewMonthValue(year, month, era);
+ CheckHebrewDayValue(year, month, day, era);
+ DateTime dt = HebrewToGregorian(year, month, day, hour, minute, second, millisecond);
+ CheckTicksRange(dt.Ticks);
+ return (dt);
+ }
+
+ private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 5790;
+
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ if (twoDigitYearMax == -1)
+ {
+ twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
+ }
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ VerifyWritable();
+ if (value == 99)
+ {
+ // Do nothing here. Year 99 is allowed so that TwoDitYearMax is disabled.
+ }
+ else
+ {
+ CheckHebrewYearValue(value, HebrewEra, nameof(value));
+ }
+ twoDigitYearMax = value;
+ }
+ }
+
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.EndContractBlock();
+
+ if (year < 100)
+ {
+ return (base.ToFourDigitYear(year));
+ }
+
+ if (year > MaxHebrewYear || year < MinHebrewYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ MinHebrewYear,
+ MaxHebrewYear));
+ }
+ return (year);
+ }
+
+ internal class __DateBuffer
+ {
+ internal int year;
+ internal int month;
+ internal int day;
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Globalization/HebrewNumber.cs b/src/mscorlib/shared/System/Globalization/HebrewNumber.cs
new file mode 100644
index 0000000000..1e8fff2bcb
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/HebrewNumber.cs
@@ -0,0 +1,457 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+
+namespace System.Globalization
+{
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Used in HebrewNumber.ParseByChar to maintain the context information (
+ // the state in the state machine and current Hebrew number values, etc.)
+ // when parsing Hebrew number character by character.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ internal struct HebrewNumberParsingContext
+ {
+ // The current state of the state machine for parsing Hebrew numbers.
+ internal HebrewNumber.HS state;
+ // The current value of the Hebrew number.
+ // The final value is determined when state is FoundEndOfHebrewNumber.
+ internal int result;
+
+ public HebrewNumberParsingContext(int result)
+ {
+ // Set the start state of the state machine for parsing Hebrew numbers.
+ state = HebrewNumber.HS.Start;
+ this.result = result;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Please see ParseByChar() for comments about different states defined here.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ internal enum HebrewNumberParsingState
+ {
+ InvalidHebrewNumber,
+ NotHebrewDigit,
+ FoundEndOfHebrewNumber,
+ ContinueParsing,
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // class HebrewNumber
+ //
+ // Provides static methods for formatting integer values into
+ // Hebrew text and parsing Hebrew number text.
+ //
+ // Limitations:
+ // Parse can only handles value 1 ~ 999.
+ // ToString() can only handles 1 ~ 999. If value is greater than 5000,
+ // 5000 will be subtracted from the value.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ internal class HebrewNumber
+ {
+ // This class contains only static methods. Add a private ctor so that
+ // compiler won't generate a default one for us.
+ private HebrewNumber()
+ {
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // ToString
+ //
+ // Converts the given number to Hebrew letters according to the numeric
+ // value of each Hebrew letter. Basically, this converts the lunar year
+ // and the lunar month to letters.
+ //
+ // The character of a year is described by three letters of the Hebrew
+ // alphabet, the first and third giving, respectively, the days of the
+ // weeks on which the New Year occurs and Passover begins, while the
+ // second is the initial of the Hebrew word for defective, normal, or
+ // complete.
+ //
+ // Defective Year : Both Heshvan and Kislev are defective (353 or 383 days)
+ // Normal Year : Heshvan is defective, Kislev is full (354 or 384 days)
+ // Complete Year : Both Heshvan and Kislev are full (355 or 385 days)
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ internal static String ToString(int Number)
+ {
+ char cTens = '\x0';
+ char cUnits; // tens and units chars
+ int Hundreds, Tens; // hundreds and tens values
+ StringBuilder szHebrew = new StringBuilder();
+
+
+ //
+ // Adjust the number if greater than 5000.
+ //
+ if (Number > 5000)
+ {
+ Number -= 5000;
+ }
+
+ Debug.Assert(Number > 0 && Number <= 999, "Number is out of range."); ;
+
+ //
+ // Get the Hundreds.
+ //
+ Hundreds = Number / 100;
+
+ if (Hundreds > 0)
+ {
+ Number -= Hundreds * 100;
+ // \x05e7 = 100
+ // \x05e8 = 200
+ // \x05e9 = 300
+ // \x05ea = 400
+ // If the number is greater than 400, use the multiples of 400.
+ for (int i = 0; i < (Hundreds / 4); i++)
+ {
+ szHebrew.Append('\x05ea');
+ }
+
+ int remains = Hundreds % 4;
+ if (remains > 0)
+ {
+ szHebrew.Append((char)((int)'\x05e6' + remains));
+ }
+ }
+
+ //
+ // Get the Tens.
+ //
+ Tens = Number / 10;
+ Number %= 10;
+
+ switch (Tens)
+ {
+ case (0):
+ cTens = '\x0';
+ break;
+ case (1):
+ cTens = '\x05d9'; // Hebrew Letter Yod
+ break;
+ case (2):
+ cTens = '\x05db'; // Hebrew Letter Kaf
+ break;
+ case (3):
+ cTens = '\x05dc'; // Hebrew Letter Lamed
+ break;
+ case (4):
+ cTens = '\x05de'; // Hebrew Letter Mem
+ break;
+ case (5):
+ cTens = '\x05e0'; // Hebrew Letter Nun
+ break;
+ case (6):
+ cTens = '\x05e1'; // Hebrew Letter Samekh
+ break;
+ case (7):
+ cTens = '\x05e2'; // Hebrew Letter Ayin
+ break;
+ case (8):
+ cTens = '\x05e4'; // Hebrew Letter Pe
+ break;
+ case (9):
+ cTens = '\x05e6'; // Hebrew Letter Tsadi
+ break;
+ }
+
+ //
+ // Get the Units.
+ //
+ cUnits = (char)(Number > 0 ? ((int)'\x05d0' + Number - 1) : 0);
+
+ if ((cUnits == '\x05d4') && // Hebrew Letter He (5)
+ (cTens == '\x05d9'))
+ { // Hebrew Letter Yod (10)
+ cUnits = '\x05d5'; // Hebrew Letter Vav (6)
+ cTens = '\x05d8'; // Hebrew Letter Tet (9)
+ }
+
+ if ((cUnits == '\x05d5') && // Hebrew Letter Vav (6)
+ (cTens == '\x05d9'))
+ { // Hebrew Letter Yod (10)
+ cUnits = '\x05d6'; // Hebrew Letter Zayin (7)
+ cTens = '\x05d8'; // Hebrew Letter Tet (9)
+ }
+
+ //
+ // Copy the appropriate info to the given buffer.
+ //
+
+ if (cTens != '\x0')
+ {
+ szHebrew.Append(cTens);
+ }
+
+ if (cUnits != '\x0')
+ {
+ szHebrew.Append(cUnits);
+ }
+
+ if (szHebrew.Length > 1)
+ {
+ szHebrew.Insert(szHebrew.Length - 1, '"');
+ }
+ else
+ {
+ szHebrew.Append('\'');
+ }
+
+ //
+ // Return success.
+ //
+ return (szHebrew.ToString());
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Token used to tokenize a Hebrew word into tokens so that we can use in the
+ // state machine.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ private enum HebrewToken : short
+ {
+ Invalid = -1,
+ Digit400 = 0,
+ Digit200_300 = 1,
+ Digit100 = 2,
+ Digit10 = 3, // 10 ~ 90
+ Digit1 = 4, // 1, 2, 3, 4, 5, 8,
+ Digit6_7 = 5,
+ Digit7 = 6,
+ Digit9 = 7,
+ SingleQuote = 8,
+ DoubleQuote = 9,
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // This class is used to map a token into its Hebrew digit value.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ private struct HebrewValue
+ {
+ internal HebrewToken token;
+ internal short value;
+ internal HebrewValue(HebrewToken token, short value)
+ {
+ this.token = token;
+ this.value = value;
+ }
+ }
+
+ //
+ // Map a Hebrew character from U+05D0 ~ U+05EA to its digit value.
+ // The value is -1 if the Hebrew character does not have a associated value.
+ //
+ private static readonly HebrewValue[] s_hebrewValues = {
+ new HebrewValue(HebrewToken.Digit1, 1) , // '\x05d0
+ new HebrewValue(HebrewToken.Digit1, 2) , // '\x05d1
+ new HebrewValue(HebrewToken.Digit1, 3) , // '\x05d2
+ new HebrewValue(HebrewToken.Digit1, 4) , // '\x05d3
+ new HebrewValue(HebrewToken.Digit1, 5) , // '\x05d4
+ new HebrewValue(HebrewToken.Digit6_7,6) , // '\x05d5
+ new HebrewValue(HebrewToken.Digit6_7,7) , // '\x05d6
+ new HebrewValue(HebrewToken.Digit1, 8) , // '\x05d7
+ new HebrewValue(HebrewToken.Digit9, 9) , // '\x05d8
+ new HebrewValue(HebrewToken.Digit10, 10) , // '\x05d9; // Hebrew Letter Yod
+ new HebrewValue(HebrewToken.Invalid, -1) , // '\x05da;
+ new HebrewValue(HebrewToken.Digit10, 20) , // '\x05db; // Hebrew Letter Kaf
+ new HebrewValue(HebrewToken.Digit10, 30) , // '\x05dc; // Hebrew Letter Lamed
+ new HebrewValue(HebrewToken.Invalid, -1) , // '\x05dd;
+ new HebrewValue(HebrewToken.Digit10, 40) , // '\x05de; // Hebrew Letter Mem
+ new HebrewValue(HebrewToken.Invalid, -1) , // '\x05df;
+ new HebrewValue(HebrewToken.Digit10, 50) , // '\x05e0; // Hebrew Letter Nun
+ new HebrewValue(HebrewToken.Digit10, 60) , // '\x05e1; // Hebrew Letter Samekh
+ new HebrewValue(HebrewToken.Digit10, 70) , // '\x05e2; // Hebrew Letter Ayin
+ new HebrewValue(HebrewToken.Invalid, -1) , // '\x05e3;
+ new HebrewValue(HebrewToken.Digit10, 80) , // '\x05e4; // Hebrew Letter Pe
+ new HebrewValue(HebrewToken.Invalid, -1) , // '\x05e5;
+ new HebrewValue(HebrewToken.Digit10, 90) , // '\x05e6; // Hebrew Letter Tsadi
+ new HebrewValue(HebrewToken.Digit100, 100) , // '\x05e7;
+ new HebrewValue(HebrewToken.Digit200_300, 200) , // '\x05e8;
+ new HebrewValue(HebrewToken.Digit200_300, 300) , // '\x05e9;
+ new HebrewValue(HebrewToken.Digit400, 400) , // '\x05ea;
+ };
+
+ private const int minHebrewNumberCh = 0x05d0;
+ private static char s_maxHebrewNumberCh = (char)(minHebrewNumberCh + s_hebrewValues.Length - 1);
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Hebrew number parsing State
+ // The current state and the next token will lead to the next state in the state machine.
+ // DQ = Double Quote
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ internal enum HS : sbyte
+ {
+ _err = -1, // an error state
+ Start = 0,
+ S400 = 1, // a Hebrew digit 400
+ S400_400 = 2, // Two Hebrew digit 400
+ S400_X00 = 3, // Two Hebrew digit 400 and followed by 100
+ S400_X0 = 4, // Hebrew digit 400 and followed by 10 ~ 90
+ X00_DQ = 5, // A hundred number and followed by a double quote.
+ S400_X00_X0 = 6,
+ X0_DQ = 7, // A two-digit number and followed by a double quote.
+ X = 8, // A single digit Hebrew number.
+ X0 = 9, // A two-digit Hebrew number
+ X00 = 10, // A three-digit Hebrew number
+ S400_DQ = 11, // A Hebrew digit 400 and followed by a double quote.
+ S400_400_DQ = 12,
+ S400_400_100 = 13,
+ S9 = 14, // Hebrew digit 9
+ X00_S9 = 15, // A hundered number and followed by a digit 9
+ S9_DQ = 16, // Hebrew digit 9 and followed by a double quote
+ END = 100, // A terminial state is reached.
+ }
+
+ //
+ // The state machine for Hebrew number pasing.
+ //
+ private static readonly HS[] s_numberPasingState =
+ {
+ // 400 300/200 100 90~10 8~1 6, 7, 9, ' "
+ /* 0 */
+ HS.S400, HS.X00, HS.X00, HS.X0, HS.X, HS.X, HS.X, HS.S9, HS._err, HS._err,
+ /* 1: S400 */
+ HS.S400_400, HS.S400_X00, HS.S400_X00, HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS.END, HS.S400_DQ,
+ /* 2: S400_400 */
+ HS._err, HS._err, HS.S400_400_100,HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS._err, HS.S400_400_DQ,
+ /* 3: S400_X00 */
+ HS._err, HS._err, HS._err, HS.S400_X00_X0, HS._err, HS._err, HS._err, HS.X00_S9 ,HS._err, HS.X00_DQ,
+ /* 4: S400_X0 */
+ HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.X0_DQ,
+ /* 5: X00_DQ */
+ HS._err, HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err,
+ /* 6: S400_X00_X0 */
+ HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.X0_DQ,
+ /* 7: X0_DQ */
+ HS._err, HS._err, HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err,
+ /* 8: X */
+ HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS._err,
+ /* 9: X0 */
+ HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.X0_DQ,
+ /* 10: X00 */
+ HS._err, HS._err, HS._err, HS.S400_X0, HS._err, HS._err, HS._err, HS.X00_S9, HS.END, HS.X00_DQ,
+ /* 11: S400_DQ */
+ HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err,
+ /* 12: S400_400_DQ*/
+ HS._err, HS._err, HS.END, HS.END, HS.END, HS.END, HS.END, HS.END, HS._err, HS._err,
+ /* 13: S400_400_100*/
+ HS._err, HS._err, HS._err, HS.S400_X00_X0, HS._err, HS._err, HS._err, HS.X00_S9, HS._err, HS.X00_DQ,
+ /* 14: S9 */
+ HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.S9_DQ,
+ /* 15: X00_S9 */
+ HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS._err, HS.S9_DQ,
+ /* 16: S9_DQ */
+ HS._err, HS._err, HS._err, HS._err, HS._err, HS.END, HS.END, HS._err, HS._err, HS._err
+ };
+
+ // Count of valid HebrewToken, column count in the NumberPasingState array
+ private const int HebrewTokenCount = 10;
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Actions:
+ // Parse the Hebrew number by passing one character at a time.
+ // The state between characters are maintained at HebrewNumberPasingContext.
+ // Returns:
+ // Return a enum of HebrewNumberParsingState.
+ // NotHebrewDigit: The specified ch is not a valid Hebrew digit.
+ // InvalidHebrewNumber: After parsing the specified ch, it will lead into
+ // an invalid Hebrew number text.
+ // FoundEndOfHebrewNumber: A terminal state is reached. This means that
+ // we find a valid Hebrew number text after the specified ch is parsed.
+ // ContinueParsing: The specified ch is a valid Hebrew digit, and
+ // it will lead into a valid state in the state machine, we should
+ // continue to parse incoming characters.
+ //
+ ////////////////////////////////////////////////////////////////////////
+
+ internal static HebrewNumberParsingState ParseByChar(char ch, ref HebrewNumberParsingContext context)
+ {
+ Debug.Assert(s_numberPasingState.Length == HebrewTokenCount * ((int)HS.S9_DQ + 1));
+
+ HebrewToken token;
+ if (ch == '\'')
+ {
+ token = HebrewToken.SingleQuote;
+ }
+ else if (ch == '\"')
+ {
+ token = HebrewToken.DoubleQuote;
+ }
+ else
+ {
+ int index = (int)ch - minHebrewNumberCh;
+ if (index >= 0 && index < s_hebrewValues.Length)
+ {
+ token = s_hebrewValues[index].token;
+ if (token == HebrewToken.Invalid)
+ {
+ return (HebrewNumberParsingState.NotHebrewDigit);
+ }
+ context.result += s_hebrewValues[index].value;
+ }
+ else
+ {
+ // Not in valid Hebrew digit range.
+ return (HebrewNumberParsingState.NotHebrewDigit);
+ }
+ }
+ context.state = s_numberPasingState[(int)context.state * (int)HebrewTokenCount + (int)token];
+ if (context.state == HS._err)
+ {
+ // Invalid Hebrew state. This indicates an incorrect Hebrew number.
+ return (HebrewNumberParsingState.InvalidHebrewNumber);
+ }
+ if (context.state == HS.END)
+ {
+ // Reach a terminal state.
+ return (HebrewNumberParsingState.FoundEndOfHebrewNumber);
+ }
+ // We should continue to parse.
+ return (HebrewNumberParsingState.ContinueParsing);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Actions:
+ // Check if the ch is a valid Hebrew number digit.
+ // This function will return true if the specified char is a legal Hebrew
+ // digit character, single quote, or double quote.
+ // Returns:
+ // true if the specified character is a valid Hebrew number character.
+ //
+ ////////////////////////////////////////////////////////////////////////
+
+ internal static bool IsDigit(char ch)
+ {
+ if (ch >= minHebrewNumberCh && ch <= s_maxHebrewNumberCh)
+ {
+ return (s_hebrewValues[ch - minHebrewNumberCh].value >= 0);
+ }
+ return (ch == '\'' || ch == '\"');
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/HijriCalendar.cs b/src/mscorlib/shared/System/Globalization/HijriCalendar.cs
new file mode 100644
index 0000000000..cafde5fbb8
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/HijriCalendar.cs
@@ -0,0 +1,677 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Rules for the Hijri calendar:
+ // - The Hijri calendar is a strictly Lunar calendar.
+ // - Days begin at sunset.
+ // - Islamic Year 1 (Muharram 1, 1 A.H.) is equivalent to absolute date
+ // 227015 (Friday, July 16, 622 C.E. - Julian).
+ // - Leap Years occur in the 2, 5, 7, 10, 13, 16, 18, 21, 24, 26, & 29th
+ // years of a 30-year cycle. Year = leap iff ((11y+14) mod 30 < 11).
+ // - There are 12 months which contain alternately 30 and 29 days.
+ // - The 12th month, Dhu al-Hijjah, contains 30 days instead of 29 days
+ // in a leap year.
+ // - Common years have 354 days. Leap years have 355 days.
+ // - There are 10,631 days in a 30-year cycle.
+ // - The Islamic months are:
+ // 1. Muharram (30 days) 7. Rajab (30 days)
+ // 2. Safar (29 days) 8. Sha'ban (29 days)
+ // 3. Rabi I (30 days) 9. Ramadan (30 days)
+ // 4. Rabi II (29 days) 10. Shawwal (29 days)
+ // 5. Jumada I (30 days) 11. Dhu al-Qada (30 days)
+ // 6. Jumada II (29 days) 12. Dhu al-Hijjah (29 days) {30}
+ //
+ // NOTENOTE
+ // The calculation of the HijriCalendar is based on the absolute date. And the
+ // absolute date means the number of days from January 1st, 1 A.D.
+ // Therefore, we do not support the days before the January 1st, 1 A.D.
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /*
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 0622/07/18 9999/12/31
+ ** Hijri 0001/01/01 9666/04/03
+ */
+
+ [Serializable]
+ public partial class HijriCalendar : Calendar
+ {
+ public static readonly int HijriEra = 1;
+
+ internal const int DatePartYear = 0;
+ internal const int DatePartDayOfYear = 1;
+ internal const int DatePartMonth = 2;
+ internal const int DatePartDay = 3;
+
+ internal const int MinAdvancedHijri = -2;
+ internal const int MaxAdvancedHijri = 2;
+
+ internal static readonly int[] HijriMonthDays = { 0, 30, 59, 89, 118, 148, 177, 207, 236, 266, 295, 325, 355 };
+
+ private int _hijriAdvance = Int32.MinValue;
+
+ // DateTime.MaxValue = Hijri calendar (year:9666, month: 4, day: 3).
+ internal const int MaxCalendarYear = 9666;
+ internal const int MaxCalendarMonth = 4;
+ internal const int MaxCalendarDay = 3;
+ // Hijri calendar (year: 1, month: 1, day:1 ) = Gregorian (year: 622, month: 7, day: 18)
+ // This is the minimal Gregorian date that we support in the HijriCalendar.
+ internal static readonly DateTime calendarMinValue = new DateTime(622, 7, 18);
+ internal static readonly DateTime calendarMaxValue = DateTime.MaxValue;
+
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (calendarMinValue);
+ }
+ }
+
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (calendarMaxValue);
+ }
+ }
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.LunarCalendar;
+ }
+ }
+
+ public HijriCalendar()
+ {
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return CalendarId.HIJRI;
+ }
+ }
+
+ protected override int DaysInYearBeforeMinSupportedYear
+ {
+ get
+ {
+ // the year before the 1st year of the cycle would have been the 30th year
+ // of the previous cycle which is not a leap year. Common years have 354 days.
+ return 354;
+ }
+ }
+
+
+
+ /*=================================GetAbsoluteDateHijri==========================
+ **Action: Gets the Absolute date for the given Hijri date. The absolute date means
+ ** the number of days from January 1st, 1 A.D.
+ **Returns:
+ **Arguments:
+ **Exceptions:
+ ============================================================================*/
+
+ private long GetAbsoluteDateHijri(int y, int m, int d)
+ {
+ return (long)(DaysUpToHijriYear(y) + HijriMonthDays[m - 1] + d - 1 - HijriAdjustment);
+ }
+
+ /*=================================DaysUpToHijriYear==========================
+ **Action: Gets the total number of days (absolute date) up to the given Hijri Year.
+ ** The absolute date means the number of days from January 1st, 1 A.D.
+ **Returns: Gets the total number of days (absolute date) up to the given Hijri Year.
+ **Arguments: HijriYear year value in Hijri calendar.
+ **Exceptions: None
+ **Notes:
+ ============================================================================*/
+
+ private long DaysUpToHijriYear(int HijriYear)
+ {
+ long NumDays; // number of absolute days
+ int NumYear30; // number of years up to current 30 year cycle
+ int NumYearsLeft; // number of years into 30 year cycle
+
+ //
+ // Compute the number of years up to the current 30 year cycle.
+ //
+ NumYear30 = ((HijriYear - 1) / 30) * 30;
+
+ //
+ // Compute the number of years left. This is the number of years
+ // into the 30 year cycle for the given year.
+ //
+ NumYearsLeft = HijriYear - NumYear30 - 1;
+
+ //
+ // Compute the number of absolute days up to the given year.
+ //
+ NumDays = ((NumYear30 * 10631L) / 30L) + 227013L;
+ while (NumYearsLeft > 0)
+ {
+ // Common year is 354 days, and leap year is 355 days.
+ NumDays += 354 + (IsLeapYear(NumYearsLeft, CurrentEra) ? 1 : 0);
+ NumYearsLeft--;
+ }
+
+ //
+ // Return the number of absolute days.
+ //
+ return (NumDays);
+ }
+
+ public int HijriAdjustment
+ {
+ get
+ {
+ if (_hijriAdvance == Int32.MinValue)
+ {
+ // Never been set before. Use the system value from registry.
+ _hijriAdvance = GetHijriDateAdjustment();
+ }
+ return (_hijriAdvance);
+ }
+
+ set
+ {
+ // NOTE: Check the value of Min/MaxAdavncedHijri with Arabic speakers to see if the assumption is good.
+ if (value < MinAdvancedHijri || value > MaxAdvancedHijri)
+ {
+ throw new ArgumentOutOfRangeException(
+ "HijriAdjustment",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Bounds_Lower_Upper,
+ MinAdvancedHijri,
+ MaxAdvancedHijri));
+ }
+ Contract.EndContractBlock();
+ VerifyWritable();
+
+ _hijriAdvance = value;
+ }
+ }
+
+ internal static void CheckTicksRange(long ticks)
+ {
+ if (ticks < calendarMinValue.Ticks || ticks > calendarMaxValue.Ticks)
+ {
+ throw new ArgumentOutOfRangeException(
+ "time",
+ String.Format(
+ CultureInfo.InvariantCulture,
+ SR.ArgumentOutOfRange_CalendarRange,
+ calendarMinValue,
+ calendarMaxValue));
+ }
+ }
+
+ internal static void CheckEraRange(int era)
+ {
+ if (era != CurrentEra && era != HijriEra)
+ {
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+ }
+
+ internal static void CheckYearRange(int year, int era)
+ {
+ CheckEraRange(era);
+ if (year < 1 || year > MaxCalendarYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ MaxCalendarYear));
+ }
+ }
+
+ internal static void CheckYearMonthRange(int year, int month, int era)
+ {
+ CheckYearRange(year, era);
+ if (year == MaxCalendarYear)
+ {
+ if (month > MaxCalendarMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(month),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ MaxCalendarMonth));
+ }
+ }
+
+ if (month < 1 || month > 12)
+ {
+ throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
+ }
+ }
+
+ /*=================================GetDatePart==========================
+ **Action: Returns a given date part of this <i>DateTime</i>. This method is used
+ ** to compute the year, day-of-year, month, or day part.
+ **Returns:
+ **Arguments:
+ **Exceptions: ArgumentException if part is incorrect.
+ **Notes:
+ ** First, we get the absolute date (the number of days from January 1st, 1 A.C) for the given ticks.
+ ** Use the formula (((AbsoluteDate - 227013) * 30) / 10631) + 1, we can a rough value for the Hijri year.
+ ** In order to get the exact Hijri year, we compare the exact absolute date for HijriYear and (HijriYear + 1).
+ ** From here, we can get the correct Hijri year.
+ ============================================================================*/
+
+ internal virtual int GetDatePart(long ticks, int part)
+ {
+ int HijriYear; // Hijri year
+ int HijriMonth; // Hijri month
+ int HijriDay; // Hijri day
+ long NumDays; // The calculation buffer in number of days.
+
+ CheckTicksRange(ticks);
+
+ //
+ // Get the absolute date. The absolute date is the number of days from January 1st, 1 A.D.
+ // 1/1/0001 is absolute date 1.
+ //
+ NumDays = ticks / GregorianCalendar.TicksPerDay + 1;
+
+ //
+ // See how much we need to backup or advance
+ //
+ NumDays += HijriAdjustment;
+
+ //
+ // Calculate the appromixate Hijri Year from this magic formula.
+ //
+ HijriYear = (int)(((NumDays - 227013) * 30) / 10631) + 1;
+
+ long daysToHijriYear = DaysUpToHijriYear(HijriYear); // The absoulte date for HijriYear
+ long daysOfHijriYear = GetDaysInYear(HijriYear, CurrentEra); // The number of days for (HijriYear+1) year.
+
+ if (NumDays < daysToHijriYear)
+ {
+ daysToHijriYear -= daysOfHijriYear;
+ HijriYear--;
+ }
+ else if (NumDays == daysToHijriYear)
+ {
+ HijriYear--;
+ daysToHijriYear -= GetDaysInYear(HijriYear, CurrentEra);
+ }
+ else
+ {
+ if (NumDays > daysToHijriYear + daysOfHijriYear)
+ {
+ daysToHijriYear += daysOfHijriYear;
+ HijriYear++;
+ }
+ }
+ if (part == DatePartYear)
+ {
+ return (HijriYear);
+ }
+
+ //
+ // Calculate the Hijri Month.
+ //
+
+ HijriMonth = 1;
+ NumDays -= daysToHijriYear;
+
+ if (part == DatePartDayOfYear)
+ {
+ return ((int)NumDays);
+ }
+
+ while ((HijriMonth <= 12) && (NumDays > HijriMonthDays[HijriMonth - 1]))
+ {
+ HijriMonth++;
+ }
+ HijriMonth--;
+
+ if (part == DatePartMonth)
+ {
+ return (HijriMonth);
+ }
+
+ //
+ // Calculate the Hijri Day.
+ //
+ HijriDay = (int)(NumDays - HijriMonthDays[HijriMonth - 1]);
+
+ if (part == DatePartDay)
+ {
+ return (HijriDay);
+ }
+ // Incorrect part value.
+ throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing);
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // months to the specified DateTime. The result is computed by incrementing
+ // (or decrementing) the year and month parts of the specified DateTime by
+ // value months, and, if required, adjusting the day part of the
+ // resulting date downwards to the last day of the resulting month in the
+ // resulting year. The time-of-day part of the result is the same as the
+ // time-of-day part of the specified DateTime.
+ //
+ // In more precise terms, considering the specified DateTime to be of the
+ // form y / m / d + t, where y is the
+ // year, m is the month, d is the day, and t is the
+ // time-of-day, the result is y1 / m1 / d1 + t,
+ // where y1 and m1 are computed by adding value months
+ // to y and m, and d1 is the largest value less than
+ // or equal to d that denotes a valid day in month m1 of year
+ // y1.
+ //
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ if (months < -120000 || months > 120000)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(months),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ -120000,
+ 120000));
+ }
+ Contract.EndContractBlock();
+ // Get the date in Hijri calendar.
+ int y = GetDatePart(time.Ticks, DatePartYear);
+ int m = GetDatePart(time.Ticks, DatePartMonth);
+ int d = GetDatePart(time.Ticks, DatePartDay);
+ int i = m - 1 + months;
+ if (i >= 0)
+ {
+ m = i % 12 + 1;
+ y = y + i / 12;
+ }
+ else
+ {
+ m = 12 + (i + 1) % 12;
+ y = y + (i - 11) / 12;
+ }
+ int days = GetDaysInMonth(y, m);
+ if (d > days)
+ {
+ d = days;
+ }
+ long ticks = GetAbsoluteDateHijri(y, m, d) * TicksPerDay + (time.Ticks % TicksPerDay);
+ Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
+ return (new DateTime(ticks));
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // years to the specified DateTime. The result is computed by incrementing
+ // (or decrementing) the year part of the specified DateTime by value
+ // years. If the month and day of the specified DateTime is 2/29, and if the
+ // resulting year is not a leap year, the month and day of the resulting
+ // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
+ // parts of the result are the same as those of the specified DateTime.
+ //
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ return (AddMonths(time, years * 12));
+ }
+
+ // Returns the day-of-month part of the specified DateTime. The returned
+ // value is an integer between 1 and 31.
+ //
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartDay));
+ }
+
+ // Returns the day-of-week part of the specified DateTime. The returned value
+ // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
+ // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
+ // Thursday, 5 indicates Friday, and 6 indicates Saturday.
+ //
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
+ }
+
+ // Returns the day-of-year part of the specified DateTime. The returned value
+ // is an integer between 1 and 366.
+ //
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartDayOfYear));
+ }
+
+ // Returns the number of days in the month given by the year and
+ // month arguments.
+ //
+ [Pure]
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ CheckYearMonthRange(year, month, era);
+ if (month == 12)
+ {
+ // For the 12th month, leap year has 30 days, and common year has 29 days.
+ return (IsLeapYear(year, CurrentEra) ? 30 : 29);
+ }
+ // Other months contain 30 and 29 days alternatively. The 1st month has 30 days.
+ return (((month % 2) == 1) ? 30 : 29);
+ }
+
+ // Returns the number of days in the year given by the year argument for the current era.
+ //
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ CheckYearRange(year, era);
+ // Common years have 354 days. Leap years have 355 days.
+ return (IsLeapYear(year, CurrentEra) ? 355 : 354);
+ }
+
+ // Returns the era for the specified DateTime value.
+
+ public override int GetEra(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+ return (HijriEra);
+ }
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (new int[] { HijriEra });
+ }
+ }
+
+ // Returns the month part of the specified DateTime. The returned value is an
+ // integer between 1 and 12.
+ //
+
+ public override int GetMonth(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartMonth));
+ }
+
+ // Returns the number of months in the specified year and era.
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ CheckYearRange(year, era);
+ return (12);
+ }
+
+ // Returns the year part of the specified DateTime. The returned value is an
+ // integer between 1 and MaxCalendarYear.
+ //
+
+ public override int GetYear(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartYear));
+ }
+
+ // Checks whether a given day in the specified era is a leap day. This method returns true if
+ // the date is a leap day, or false if not.
+ //
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ // The year/month/era value checking is done in GetDaysInMonth().
+ int daysInMonth = GetDaysInMonth(year, month, era);
+ if (day < 1 || day > daysInMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Day,
+ daysInMonth,
+ month));
+ }
+ return (IsLeapYear(year, era) && month == 12 && day == 30);
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this calendar does not have leap month, or this year is not a leap year.
+ //
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ CheckYearRange(year, era);
+ return (0);
+ }
+
+ // Checks whether a given month in the specified era is a leap month. This method returns true if
+ // month is a leap month, or false if not.
+ //
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ CheckYearMonthRange(year, month, era);
+ return (false);
+ }
+
+ // Checks whether a given year in the specified era is a leap year. This method returns true if
+ // year is a leap year, or false if not.
+ //
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ CheckYearRange(year, era);
+ return ((((year * 11) + 14) % 30) < 11);
+ }
+
+ // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
+ //
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ // The year/month/era checking is done in GetDaysInMonth().
+ int daysInMonth = GetDaysInMonth(year, month, era);
+ if (day < 1 || day > daysInMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Day,
+ daysInMonth,
+ month));
+ }
+
+ long lDate = GetAbsoluteDateHijri(year, month, day);
+
+ if (lDate >= 0)
+ {
+ return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond)));
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
+ }
+ }
+
+ private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1451;
+
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ if (twoDigitYearMax == -1)
+ {
+ twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
+ }
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ VerifyWritable();
+ if (value < 99 || value > MaxCalendarYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(value),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 99,
+ MaxCalendarYear));
+ }
+ twoDigitYearMax = value;
+ }
+ }
+
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.EndContractBlock();
+
+ if (year < 100)
+ {
+ return (base.ToFourDigitYear(year));
+ }
+
+ if (year > MaxCalendarYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ MaxCalendarYear));
+ }
+ return (year);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/InternalGlobalizationHelper.cs b/src/mscorlib/shared/System/Globalization/InternalGlobalizationHelper.cs
new file mode 100644
index 0000000000..f5eea1b629
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/InternalGlobalizationHelper.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ internal class InternalGlobalizationHelper
+ {
+ // Copied from the TimeSpan to be used inside the globalization code and avoid internal dependancy on TimeSpan class
+ internal static long TimeToTicks(int hour, int minute, int second)
+ {
+ // totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
+ // which is less than 2^44, meaning we won't overflow totalSeconds.
+ long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second;
+ if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds)
+ throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
+ return totalSeconds * TicksPerSecond;
+ }
+
+
+ //
+ // Define needed constants so globalization code can be independant from any other types
+ //
+
+ internal const long TicksPerMillisecond = 10000;
+ internal const long TicksPerTenthSecond = TicksPerMillisecond * 100;
+ internal const long TicksPerSecond = TicksPerMillisecond * 1000; // 10,000,000
+ internal const long MaxSeconds = Int64.MaxValue / TicksPerSecond;
+ internal const long MinSeconds = Int64.MinValue / TicksPerSecond;
+ private const int DaysPerYear = 365;
+ private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461
+ private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524
+ private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097
+ private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059
+ private const long TicksPerMinute = TicksPerSecond * 60;
+ private const long TicksPerHour = TicksPerMinute * 60;
+ private const long TicksPerDay = TicksPerHour * 24;
+ internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1;
+ internal const long MinTicks = 0;
+ internal const long MaxMilliSeconds = Int64.MaxValue / TicksPerMillisecond;
+ internal const long MinMilliSeconds = Int64.MinValue / TicksPerMillisecond;
+
+ internal const int StringBuilderDefaultCapacity = 16;
+
+ internal const Int64 MaxOffset = TimeSpan.TicksPerHour * 14;
+ internal const Int64 MinOffset = -MaxOffset;
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/JapaneseCalendar.cs b/src/mscorlib/shared/System/Globalization/JapaneseCalendar.cs
new file mode 100644
index 0000000000..4655e08a4e
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/JapaneseCalendar.cs
@@ -0,0 +1,409 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ /*=================================JapaneseCalendar==========================
+ **
+ ** JapaneseCalendar is based on Gregorian calendar. The month and day values are the same as
+ ** Gregorian calendar. However, the year value is an offset to the Gregorian
+ ** year based on the era.
+ **
+ ** This system is adopted by Emperor Meiji in 1868. The year value is counted based on the reign of an emperor,
+ ** and the era begins on the day an emperor ascends the throne and continues until his death.
+ ** The era changes at 12:00AM.
+ **
+ ** For example, the current era is Heisei. It started on 1989/1/8 A.D. Therefore, Gregorian year 1989 is also Heisei 1st.
+ ** 1989/1/8 A.D. is also Heisei 1st 1/8.
+ **
+ ** Any date in the year during which era is changed can be reckoned in either era. For example,
+ ** 1989/1/1 can be 1/1 Heisei 1st year or 1/1 Showa 64th year.
+ **
+ ** Note:
+ ** The DateTime can be represented by the JapaneseCalendar are limited to two factors:
+ ** 1. The min value and max value of DateTime class.
+ ** 2. The available era information.
+ **
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 1868/09/08 9999/12/31
+ ** Japanese Meiji 01/01 Heisei 8011/12/31
+ ============================================================================*/
+
+
+ [Serializable]
+ public partial class JapaneseCalendar : Calendar
+ {
+ internal static readonly DateTime calendarMinValue = new DateTime(1868, 9, 8);
+
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (calendarMinValue);
+ }
+ }
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (DateTime.MaxValue);
+ }
+ }
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.SolarCalendar;
+ }
+ }
+
+ //
+ // Using a field initializer rather than a static constructor so that the whole class can be lazy
+ // init.
+ internal static volatile EraInfo[] japaneseEraInfo;
+
+ //
+ // Read our era info
+ //
+ // m_EraInfo must be listed in reverse chronological order. The most recent era
+ // should be the first element.
+ // That is, m_EraInfo[0] contains the most recent era.
+ //
+ // We know about 4 built-in eras, however users may add additional era(s) from the
+ // registry, by adding values to HKLM\SYSTEM\CurrentControlSet\Control\Nls\Calendars\Japanese\Eras
+ // we don't read the registry and instead we call WinRT to get the needed informatio
+ //
+ // Registry values look like:
+ // yyyy.mm.dd=era_abbrev_english_englishabbrev
+ //
+ // Where yyyy.mm.dd is the registry value name, and also the date of the era start.
+ // yyyy, mm, and dd are the year, month & day the era begins (4, 2 & 2 digits long)
+ // era is the Japanese Era name
+ // abbrev is the Abbreviated Japanese Era Name
+ // english is the English name for the Era (unused)
+ // englishabbrev is the Abbreviated English name for the era.
+ // . is a delimiter, but the value of . doesn't matter.
+ // '_' marks the space between the japanese era name, japanese abbreviated era name
+ // english name, and abbreviated english names.
+ //
+ internal static EraInfo[] GetEraInfo()
+ {
+ // See if we need to build it
+ if (japaneseEraInfo == null)
+ {
+ japaneseEraInfo = GetJapaneseEras();
+ // See if we have to use the built-in eras
+ if (japaneseEraInfo == null)
+ {
+ // We know about some built-in ranges
+ EraInfo[] defaultEraRanges = new EraInfo[4];
+ defaultEraRanges[0] = new EraInfo(4, 1989, 1, 8, 1988, 1, GregorianCalendar.MaxYear - 1988,
+ "\x5e73\x6210", "\x5e73", "H"); // era #4 start year/month/day, yearOffset, minEraYear
+ defaultEraRanges[1] = new EraInfo(3, 1926, 12, 25, 1925, 1, 1989 - 1925,
+ "\x662d\x548c", "\x662d", "S"); // era #3,start year/month/day, yearOffset, minEraYear
+ defaultEraRanges[2] = new EraInfo(2, 1912, 7, 30, 1911, 1, 1926 - 1911,
+ "\x5927\x6b63", "\x5927", "T"); // era #2,start year/month/day, yearOffset, minEraYear
+ defaultEraRanges[3] = new EraInfo(1, 1868, 1, 1, 1867, 1, 1912 - 1867,
+ "\x660e\x6cbb", "\x660e", "M"); // era #1,start year/month/day, yearOffset, minEraYear
+
+ // Remember the ranges we built
+ japaneseEraInfo = defaultEraRanges;
+ }
+ }
+
+ // return the era we found/made
+ return japaneseEraInfo;
+ }
+
+ internal static volatile Calendar s_defaultInstance;
+ internal GregorianCalendarHelper helper;
+
+ /*=================================GetDefaultInstance==========================
+ **Action: Internal method to provide a default intance of JapaneseCalendar. Used by NLS+ implementation
+ ** and other calendars.
+ **Returns:
+ **Arguments:
+ **Exceptions:
+ ============================================================================*/
+
+ internal static Calendar GetDefaultInstance()
+ {
+ if (s_defaultInstance == null)
+ {
+ s_defaultInstance = new JapaneseCalendar();
+ }
+ return (s_defaultInstance);
+ }
+
+
+ public JapaneseCalendar()
+ {
+ try
+ {
+ new CultureInfo("ja-JP");
+ }
+ catch (ArgumentException e)
+ {
+ throw new TypeInitializationException(this.GetType().ToString(), e);
+ }
+ helper = new GregorianCalendarHelper(this, GetEraInfo());
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return CalendarId.JAPAN;
+ }
+ }
+
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ return (helper.AddMonths(time, months));
+ }
+
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ return (helper.AddYears(time, years));
+ }
+
+ /*=================================GetDaysInMonth==========================
+ **Action: Returns the number of days in the month given by the year and month arguments.
+ **Returns: The number of days in the given month.
+ **Arguments:
+ ** year The year in Japanese calendar.
+ ** month The month
+ ** era The Japanese era value.
+ **Exceptions
+ ** ArgumentException If month is less than 1 or greater * than 12.
+ ============================================================================*/
+
+
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ return (helper.GetDaysInMonth(year, month, era));
+ }
+
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ return (helper.GetDaysInYear(year, era));
+ }
+
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ return (helper.GetDayOfMonth(time));
+ }
+
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ return (helper.GetDayOfWeek(time));
+ }
+
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ return (helper.GetDayOfYear(time));
+ }
+
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ return (helper.GetMonthsInYear(year, era));
+ }
+
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
+ {
+ return (helper.GetWeekOfYear(time, rule, firstDayOfWeek));
+ }
+
+ /*=================================GetEra==========================
+ **Action: Get the era value of the specified time.
+ **Returns: The era value for the specified time.
+ **Arguments:
+ ** time the specified date time.
+ **Exceptions: ArgumentOutOfRangeException if time is out of the valid era ranges.
+ ============================================================================*/
+
+
+ public override int GetEra(DateTime time)
+ {
+ return (helper.GetEra(time));
+ }
+
+
+ public override int GetMonth(DateTime time)
+ {
+ return (helper.GetMonth(time));
+ }
+
+
+ public override int GetYear(DateTime time)
+ {
+ return (helper.GetYear(time));
+ }
+
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ return (helper.IsLeapDay(year, month, day, era));
+ }
+
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ return (helper.IsLeapYear(year, era));
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this calendar does not have leap month, or this year is not a leap year.
+ //
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ return (helper.GetLeapMonth(year, era));
+ }
+
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ return (helper.IsLeapMonth(year, month, era));
+ }
+
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era));
+ }
+
+ // For Japanese calendar, four digit year is not used. Few emperors will live for more than one hundred years.
+ // Therefore, for any two digit number, we just return the original number.
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedPosNum);
+ }
+ Contract.EndContractBlock();
+
+ if (year > helper.MaxYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ helper.MaxYear));
+ }
+ return (year);
+ }
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (helper.Eras);
+ }
+ }
+
+ //
+ // Return the various era strings
+ // Note: The arrays are backwards of the eras
+ //
+ internal static String[] EraNames()
+ {
+ EraInfo[] eras = GetEraInfo();
+ String[] eraNames = new String[eras.Length];
+
+ for (int i = 0; i < eras.Length; i++)
+ {
+ // Strings are in chronological order, eras are backwards order.
+ eraNames[i] = eras[eras.Length - i - 1].eraName;
+ }
+
+ return eraNames;
+ }
+
+ internal static String[] AbbrevEraNames()
+ {
+ EraInfo[] eras = GetEraInfo();
+ String[] erasAbbrev = new String[eras.Length];
+
+ for (int i = 0; i < eras.Length; i++)
+ {
+ // Strings are in chronological order, eras are backwards order.
+ erasAbbrev[i] = eras[eras.Length - i - 1].abbrevEraName;
+ }
+
+ return erasAbbrev;
+ }
+
+ internal static String[] EnglishEraNames()
+ {
+ EraInfo[] eras = GetEraInfo();
+ String[] erasEnglish = new String[eras.Length];
+
+ for (int i = 0; i < eras.Length; i++)
+ {
+ // Strings are in chronological order, eras are backwards order.
+ erasEnglish[i] = eras[eras.Length - i - 1].englishEraName;
+ }
+
+ return erasEnglish;
+ }
+
+ private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 99;
+
+ internal override bool IsValidYear(int year, int era)
+ {
+ return helper.IsValidYear(year, era);
+ }
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ if (twoDigitYearMax == -1)
+ {
+ twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
+ }
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ VerifyWritable();
+ if (value < 99 || value > helper.MaxYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ "year",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 99,
+ helper.MaxYear));
+ }
+ twoDigitYearMax = value;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/JapaneseLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/JapaneseLunisolarCalendar.cs
new file mode 100644
index 0000000000..95e87f85d7
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/JapaneseLunisolarCalendar.cs
@@ -0,0 +1,305 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ /*
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 1960/01/28 2050/01/22
+ ** JapaneseLunisolar 1960/01/01 2049/12/29
+ */
+
+ [Serializable]
+ public class JapaneseLunisolarCalendar : EastAsianLunisolarCalendar
+ {
+ //
+ // The era value for the current era.
+ //
+
+ public const int JapaneseEra = 1;
+
+ internal GregorianCalendarHelper helper;
+
+ internal const int MIN_LUNISOLAR_YEAR = 1960;
+ internal const int MAX_LUNISOLAR_YEAR = 2049;
+
+ internal const int MIN_GREGORIAN_YEAR = 1960;
+ internal const int MIN_GREGORIAN_MONTH = 1;
+ internal const int MIN_GREGORIAN_DAY = 28;
+
+ internal const int MAX_GREGORIAN_YEAR = 2050;
+ internal const int MAX_GREGORIAN_MONTH = 1;
+ internal const int MAX_GREGORIAN_DAY = 22;
+
+ internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY);
+ internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999);
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+ protected override int DaysInYearBeforeMinSupportedYear
+ {
+ get
+ {
+ // 1959 from ChineseLunisolarCalendar
+ return 354;
+ }
+ }
+
+ private static readonly int[,] s_yinfo =
+ {
+ /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days
+ 1960 */
+ { 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1962 */{ 0 , 2 , 5 , 19808 },/* 29 30 29 29 30 30 29 30 29 30 30 29 0 354
+1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1965 */{ 0 , 2 , 2 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354
+1966 */{ 3 , 1 , 22 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383
+1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355
+1968 */{ 7 , 1 , 30 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384
+1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1970 */{ 0 , 2 , 6 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355
+1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1972 */{ 0 , 2 , 15 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1973 */{ 0 , 2 , 3 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354
+1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1976 */{ 8 , 1 , 31 , 54600 },/* 30 30 29 30 29 30 29 30 29 30 29 29 30 384
+1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354
+1978 */{ 0 , 2 , 7 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355
+1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384
+1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1982 */{ 4 , 1 , 25 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+1987 */{ 6 , 1 , 29 , 46504 },/* 30 29 30 30 29 30 29 30 30 29 30 29 30 385
+1988 */{ 0 , 2 , 18 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1989 */{ 0 , 2 , 6 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1990 */{ 5 , 1 , 27 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384
+1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355
+1995 */{ 8 , 1 , 31 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384
+1996 */{ 0 , 2 , 19 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355
+1997 */{ 0 , 2 , 8 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1998 */{ 5 , 1 , 28 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384
+1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+2001 */{ 4 , 1 , 24 , 58536 },/* 30 30 30 29 29 30 29 29 30 29 30 29 30 384
+2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+2005 */{ 0 , 2 , 9 , 22208 },/* 29 30 29 30 29 30 30 29 30 30 29 29 0 354
+2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385
+2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354
+2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384
+2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+2012 */{ 3 , 1 , 23 , 47696 },/* 30 29 30 30 30 29 30 29 29 30 29 30 29 384
+2013 */{ 0 , 2 , 10 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384
+2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354
+2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+2017 */{ 5 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+2019 */{ 0 , 2 , 5 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+2020 */{ 4 , 1 , 25 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384
+2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+2023 */{ 2 , 1 , 22 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384
+2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+2026 */{ 0 , 2 , 17 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+2027 */{ 0 , 2 , 7 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354
+2028 */{ 5 , 1 , 27 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383
+2029 */{ 0 , 2 , 13 , 55600 },/* 30 30 29 30 30 29 29 30 29 29 30 30 0 355
+2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+2031 */{ 3 , 1 , 23 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384
+2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+2034 */{ 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+2036 */{ 6 , 1 , 28 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354
+2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384
+2040 */{ 0 , 2 , 12 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355
+2041 */{ 0 , 2 , 1 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384
+2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+2046 */{ 0 , 2 , 6 , 45648 },/* 30 29 30 30 29 29 30 29 29 30 29 30 0 354
+2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+2048 */{ 0 , 2 , 14 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355
+ */ };
+
+ internal override int MinCalendarYear
+ {
+ get
+ {
+ return (MIN_LUNISOLAR_YEAR);
+ }
+ }
+
+ internal override int MaxCalendarYear
+ {
+ get
+ {
+ return (MAX_LUNISOLAR_YEAR);
+ }
+ }
+
+ internal override DateTime MinDate
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+ internal override DateTime MaxDate
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+
+ internal override EraInfo[] CalEraInfo
+ {
+ get
+ {
+ return (JapaneseCalendar.GetEraInfo());
+ }
+ }
+
+ internal override int GetYearInfo(int lunarYear, int index)
+ {
+ if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR))
+ {
+ throw new ArgumentOutOfRangeException(
+ "year",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ MIN_LUNISOLAR_YEAR,
+ MAX_LUNISOLAR_YEAR));
+ }
+ Contract.EndContractBlock();
+
+ return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index];
+ }
+
+ internal override int GetYear(int year, DateTime time)
+ {
+ return helper.GetYear(year, time);
+ }
+
+ internal override int GetGregorianYear(int year, int era)
+ {
+ return helper.GetGregorianYear(year, era);
+ }
+
+ // Trim off the eras that are before our date range
+ private static EraInfo[] TrimEras(EraInfo[] baseEras)
+ {
+ EraInfo[] newEras = new EraInfo[baseEras.Length];
+ int newIndex = 0;
+
+ // Eras have most recent first, so start with that
+ for (int i = 0; i < baseEras.Length; i++)
+ {
+ // If this one's minimum year is bigger than our maximum year
+ // then we can't use it.
+ if (baseEras[i].yearOffset + baseEras[i].minEraYear >= MAX_LUNISOLAR_YEAR)
+ {
+ // skip this one.
+ continue;
+ }
+
+ // If this one's maximum era is less than our minimum era
+ // then we've gotten too low in the era #s, so we're done
+ if (baseEras[i].yearOffset + baseEras[i].maxEraYear < MIN_LUNISOLAR_YEAR)
+ {
+ break;
+ }
+
+ // Wasn't too large or too small, can use this one
+ newEras[newIndex] = baseEras[i];
+ newIndex++;
+ }
+
+ // If we didn't copy any then something was wrong, just return base
+ if (newIndex == 0) return baseEras;
+
+ // Resize the output array
+ Array.Resize(ref newEras, newIndex);
+ return newEras;
+ }
+
+
+ // Construct an instance of JapaneseLunisolar calendar.
+ public JapaneseLunisolarCalendar()
+ {
+ helper = new GregorianCalendarHelper(this, TrimEras(JapaneseCalendar.GetEraInfo()));
+ }
+
+
+ public override int GetEra(DateTime time)
+ {
+ return (helper.GetEra(time));
+ }
+
+ internal override CalendarId BaseCalendarID
+ {
+ get
+ {
+ return (CalendarId.JAPAN);
+ }
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return (CalendarId.JAPANESELUNISOLAR);
+ }
+ }
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (helper.Eras);
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/JulianCalendar.cs b/src/mscorlib/shared/System/Globalization/JulianCalendar.cs
new file mode 100644
index 0000000000..f4678c1a85
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/JulianCalendar.cs
@@ -0,0 +1,443 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ //
+ // This class implements the Julian calendar. In 48 B.C. Julius Caesar ordered a calendar reform, and this calendar
+ // is called Julian calendar. It consisted of a solar year of twelve months and of 365 days with an extra day
+ // every fourth year.
+ //*
+ //* Calendar support range:
+ //* Calendar Minimum Maximum
+ //* ========== ========== ==========
+ //* Gregorian 0001/01/01 9999/12/31
+ //* Julia 0001/01/03 9999/10/19
+
+ [Serializable]
+ public class JulianCalendar : Calendar
+ {
+ public static readonly int JulianEra = 1;
+
+ private const int DatePartYear = 0;
+ private const int DatePartDayOfYear = 1;
+ private const int DatePartMonth = 2;
+ private const int DatePartDay = 3;
+
+ // Number of days in a non-leap year
+ private const int JulianDaysPerYear = 365;
+ // Number of days in 4 years
+ private const int JulianDaysPer4Years = JulianDaysPerYear * 4 + 1;
+
+ private static readonly int[] s_daysToMonth365 =
+ {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
+ };
+
+ private static readonly int[] s_daysToMonth366 =
+ {
+ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
+ };
+
+ // Gregorian Calendar 9999/12/31 = Julian Calendar 9999/10/19
+ // keep it as variable field for serialization compat.
+ internal int MaxYear = 9999;
+
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (DateTime.MinValue);
+ }
+ }
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (DateTime.MaxValue);
+ }
+ }
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.SolarCalendar;
+ }
+ }
+
+ public JulianCalendar()
+ {
+ // There is no system setting of TwoDigitYear max, so set the value here.
+ twoDigitYearMax = 2029;
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return CalendarId.JULIAN;
+ }
+ }
+
+ internal static void CheckEraRange(int era)
+ {
+ if (era != CurrentEra && era != JulianEra)
+ {
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+ }
+
+ internal void CheckYearEraRange(int year, int era)
+ {
+ CheckEraRange(era);
+ if (year <= 0 || year > MaxYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ MaxYear));
+ }
+ }
+
+ internal static void CheckMonthRange(int month)
+ {
+ if (month < 1 || month > 12)
+ {
+ throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
+ }
+ }
+
+ /*===================================CheckDayRange============================
+ **Action: Check for if the day value is valid.
+ **Returns:
+ **Arguments:
+ **Exceptions:
+ **Notes:
+ ** Before calling this method, call CheckYearEraRange()/CheckMonthRange() to make
+ ** sure year/month values are correct.
+ ============================================================================*/
+
+ internal static void CheckDayRange(int year, int month, int day)
+ {
+ if (year == 1 && month == 1)
+ {
+ // The mimimum supported Julia date is Julian 0001/01/03.
+ if (day < 3)
+ {
+ throw new ArgumentOutOfRangeException(null,
+ SR.ArgumentOutOfRange_BadYearMonthDay);
+ }
+ }
+ bool isLeapYear = (year % 4) == 0;
+ int[] days = isLeapYear ? s_daysToMonth366 : s_daysToMonth365;
+ int monthDays = days[month] - days[month - 1];
+ if (day < 1 || day > monthDays)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ monthDays));
+ }
+ }
+
+
+ // Returns a given date part of this DateTime. This method is used
+ // to compute the year, day-of-year, month, or day part.
+ internal static int GetDatePart(long ticks, int part)
+ {
+ // Gregorian 1/1/0001 is Julian 1/3/0001. Remember DateTime(0) is refered to Gregorian 1/1/0001.
+ // The following line convert Gregorian ticks to Julian ticks.
+ long julianTicks = ticks + TicksPerDay * 2;
+ // n = number of days since 1/1/0001
+ int n = (int)(julianTicks / TicksPerDay);
+ // y4 = number of whole 4-year periods within 100-year period
+ int y4 = n / JulianDaysPer4Years;
+ // n = day number within 4-year period
+ n -= y4 * JulianDaysPer4Years;
+ // y1 = number of whole years within 4-year period
+ int y1 = n / JulianDaysPerYear;
+ // Last year has an extra day, so decrement result if 4
+ if (y1 == 4) y1 = 3;
+ // If year was requested, compute and return it
+ if (part == DatePartYear)
+ {
+ return (y4 * 4 + y1 + 1);
+ }
+ // n = day number within year
+ n -= y1 * JulianDaysPerYear;
+ // If day-of-year was requested, return it
+ if (part == DatePartDayOfYear)
+ {
+ return (n + 1);
+ }
+ // Leap year calculation looks different from IsLeapYear since y1, y4,
+ // and y100 are relative to year 1, not year 0
+ bool leapYear = (y1 == 3);
+ int[] days = leapYear ? s_daysToMonth366 : s_daysToMonth365;
+ // All months have less than 32 days, so n >> 5 is a good conservative
+ // estimate for the month
+ int m = (n >> 5) + 1;
+ // m = 1-based month number
+ while (n >= days[m]) m++;
+ // If month was requested, return it
+ if (part == DatePartMonth) return (m);
+ // Return 1-based day-of-month
+ return (n - days[m - 1] + 1);
+ }
+
+ // Returns the tick count corresponding to the given year, month, and day.
+ internal static long DateToTicks(int year, int month, int day)
+ {
+ int[] days = (year % 4 == 0) ? s_daysToMonth366 : s_daysToMonth365;
+ int y = year - 1;
+ int n = y * 365 + y / 4 + days[month - 1] + day - 1;
+ // Gregorian 1/1/0001 is Julian 1/3/0001. n * TicksPerDay is the ticks in JulianCalendar.
+ // Therefore, we subtract two days in the following to convert the ticks in JulianCalendar
+ // to ticks in Gregorian calendar.
+ return ((n - 2) * TicksPerDay);
+ }
+
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ if (months < -120000 || months > 120000)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(months),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ -120000,
+ 120000));
+ }
+ Contract.EndContractBlock();
+ int y = GetDatePart(time.Ticks, DatePartYear);
+ int m = GetDatePart(time.Ticks, DatePartMonth);
+ int d = GetDatePart(time.Ticks, DatePartDay);
+ int i = m - 1 + months;
+ if (i >= 0)
+ {
+ m = i % 12 + 1;
+ y = y + i / 12;
+ }
+ else
+ {
+ m = 12 + (i + 1) % 12;
+ y = y + (i - 11) / 12;
+ }
+ int[] daysArray = (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) ? s_daysToMonth366 : s_daysToMonth365;
+ int days = (daysArray[m] - daysArray[m - 1]);
+
+ if (d > days)
+ {
+ d = days;
+ }
+ long ticks = DateToTicks(y, m, d) + time.Ticks % TicksPerDay;
+ Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
+ return (new DateTime(ticks));
+ }
+
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ return (AddMonths(time, years * 12));
+ }
+
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartDay));
+ }
+
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
+ }
+
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartDayOfYear));
+ }
+
+
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ CheckYearEraRange(year, era);
+ CheckMonthRange(month);
+ int[] days = (year % 4 == 0) ? s_daysToMonth366 : s_daysToMonth365;
+ return (days[month] - days[month - 1]);
+ }
+
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ // Year/Era range is done in IsLeapYear().
+ return (IsLeapYear(year, era) ? 366 : 365);
+ }
+
+
+ public override int GetEra(DateTime time)
+ {
+ return (JulianEra);
+ }
+
+
+ public override int GetMonth(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartMonth));
+ }
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (new int[] { JulianEra });
+ }
+ }
+
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ CheckYearEraRange(year, era);
+ return (12);
+ }
+
+
+ public override int GetYear(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartYear));
+ }
+
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ CheckMonthRange(month);
+ // Year/Era range check is done in IsLeapYear().
+ if (IsLeapYear(year, era))
+ {
+ CheckDayRange(year, month, day);
+ return (month == 2 && day == 29);
+ }
+ CheckDayRange(year, month, day);
+ return (false);
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this calendar does not have leap month, or this year is not a leap year.
+ //
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ CheckYearEraRange(year, era);
+ return (0);
+ }
+
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ CheckYearEraRange(year, era);
+ CheckMonthRange(month);
+ return (false);
+ }
+
+ // Checks whether a given year in the specified era is a leap year. This method returns true if
+ // year is a leap year, or false if not.
+ //
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ CheckYearEraRange(year, era);
+ return (year % 4 == 0);
+ }
+
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ CheckYearEraRange(year, era);
+ CheckMonthRange(month);
+ CheckDayRange(year, month, day);
+ if (millisecond < 0 || millisecond >= MillisPerSecond)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(millisecond),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 0,
+ MillisPerSecond - 1));
+ }
+
+ if (hour >= 0 && hour < 24 && minute >= 0 && minute < 60 && second >= 0 && second < 60)
+ {
+ return new DateTime(DateToTicks(year, month, day) + (new TimeSpan(0, hour, minute, second, millisecond)).Ticks);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadHourMinuteSecond);
+ }
+ }
+
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ VerifyWritable();
+ if (value < 99 || value > MaxYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ "year",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 99,
+ MaxYear));
+ }
+ twoDigitYearMax = value;
+ }
+ }
+
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.EndContractBlock();
+
+ if (year > MaxYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Bounds_Lower_Upper,
+ 1,
+ MaxYear));
+ }
+ return (base.ToFourDigitYear(year));
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/KoreanCalendar.cs b/src/mscorlib/shared/System/Globalization/KoreanCalendar.cs
new file mode 100644
index 0000000000..b962b1c427
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/KoreanCalendar.cs
@@ -0,0 +1,266 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ /*=================================KoreanCalendar==========================
+ **
+ ** Korean calendar is based on the Gregorian calendar. And the year is an offset to Gregorian calendar.
+ ** That is,
+ ** Korean year = Gregorian year + 2333. So 2000/01/01 A.D. is Korean 4333/01/01
+ **
+ ** 0001/1/1 A.D. is Korean year 2334.
+ **
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 0001/01/01 9999/12/31
+ ** Korean 2334/01/01 12332/12/31
+ ============================================================================*/
+
+
+ [Serializable]
+ public class KoreanCalendar : Calendar
+ {
+ //
+ // The era value for the current era.
+ //
+
+ public const int KoreanEra = 1;
+
+ // Since
+ // Gregorian Year = Era Year + yearOffset
+ // Gregorian Year 1 is Korean year 2334, so that
+ // 1 = 2334 + yearOffset
+ // So yearOffset = -2333
+ // Gregorian year 2001 is Korean year 4334.
+
+ //m_EraInfo[0] = new EraInfo(1, new DateTime(1, 1, 1).Ticks, -2333, 2334, GregorianCalendar.MaxYear + 2333);
+
+ // Initialize our era info.
+ internal static EraInfo[] koreanEraInfo = new EraInfo[] {
+ new EraInfo( 1, 1, 1, 1, -2333, 2334, GregorianCalendar.MaxYear + 2333) // era #, start year/month/day, yearOffset, minEraYear
+ };
+
+ internal GregorianCalendarHelper helper;
+
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (DateTime.MinValue);
+ }
+ }
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (DateTime.MaxValue);
+ }
+ }
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.SolarCalendar;
+ }
+ }
+
+ public KoreanCalendar()
+ {
+ try
+ {
+ new CultureInfo("ko-KR");
+ }
+ catch (ArgumentException e)
+ {
+ throw new TypeInitializationException(this.GetType().ToString(), e);
+ }
+ helper = new GregorianCalendarHelper(this, koreanEraInfo);
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return CalendarId.KOREA;
+ }
+ }
+
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ return (helper.AddMonths(time, months));
+ }
+
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ return (helper.AddYears(time, years));
+ }
+
+ /*=================================GetDaysInMonth==========================
+ **Action: Returns the number of days in the month given by the year and month arguments.
+ **Returns: The number of days in the given month.
+ **Arguments:
+ ** year The year in Korean calendar.
+ ** month The month
+ ** era The Japanese era value.
+ **Exceptions
+ ** ArgumentException If month is less than 1 or greater * than 12.
+ ============================================================================*/
+
+
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ return (helper.GetDaysInMonth(year, month, era));
+ }
+
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ return (helper.GetDaysInYear(year, era));
+ }
+
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ return (helper.GetDayOfMonth(time));
+ }
+
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ return (helper.GetDayOfWeek(time));
+ }
+
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ return (helper.GetDayOfYear(time));
+ }
+
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ return (helper.GetMonthsInYear(year, era));
+ }
+
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
+ {
+ return (helper.GetWeekOfYear(time, rule, firstDayOfWeek));
+ }
+
+
+ public override int GetEra(DateTime time)
+ {
+ return (helper.GetEra(time));
+ }
+
+ public override int GetMonth(DateTime time)
+ {
+ return (helper.GetMonth(time));
+ }
+
+
+ public override int GetYear(DateTime time)
+ {
+ return (helper.GetYear(time));
+ }
+
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ return (helper.IsLeapDay(year, month, day, era));
+ }
+
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ return (helper.IsLeapYear(year, era));
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this calendar does not have leap month, or this year is not a leap year.
+ //
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ return (helper.GetLeapMonth(year, era));
+ }
+
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ return (helper.IsLeapMonth(year, month, era));
+ }
+
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era));
+ }
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (helper.Eras);
+ }
+ }
+
+ private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 4362;
+
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ if (twoDigitYearMax == -1)
+ {
+ twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
+ }
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ VerifyWritable();
+ if (value < 99 || value > helper.MaxYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ "year",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 99,
+ helper.MaxYear));
+ }
+ twoDigitYearMax = value;
+ }
+ }
+
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.EndContractBlock();
+
+ return (helper.ToFourDigitYear(year, this.TwoDigitYearMax));
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/KoreanLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/KoreanLunisolarCalendar.cs
new file mode 100644
index 0000000000..d4c71632aa
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/KoreanLunisolarCalendar.cs
@@ -0,0 +1,1323 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ /*
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 918/02/14 2051/02/10
+ ** KoreanLunisolar 918/01/01 2050/13/29
+ */
+
+ [Serializable]
+ public class KoreanLunisolarCalendar : EastAsianLunisolarCalendar
+ {
+ //
+ // The era value for the current era.
+ //
+
+ public const int GregorianEra = 1;
+
+ internal const int MIN_LUNISOLAR_YEAR = 918;
+ internal const int MAX_LUNISOLAR_YEAR = 2050;
+
+ internal const int MIN_GREGORIAN_YEAR = 918;
+ internal const int MIN_GREGORIAN_MONTH = 2;
+ internal const int MIN_GREGORIAN_DAY = 14;
+
+ internal const int MAX_GREGORIAN_YEAR = 2051;
+ internal const int MAX_GREGORIAN_MONTH = 2;
+ internal const int MAX_GREGORIAN_DAY = 10;
+
+ internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY);
+ internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999);
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+
+ protected override int DaysInYearBeforeMinSupportedYear
+ {
+ get
+ {
+ // 917 -- From http://emr.cs.iit.edu/home/reingold/calendar-book/Calendrica.html
+ // using ChineseLunisolar
+ return 384;
+ }
+ }
+
+ private static readonly int[,] s_yinfo =
+ {
+ /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days
+ 918 */
+ { 0 , 2 , 14 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355
+919 */{ 0 , 2 , 4 , 17872 },/* 29 30 29 29 29 30 29 30 30 30 29 30 0 354
+920 */{ 6 , 1 , 24 , 41688 },/* 30 29 30 29 29 29 30 29 30 30 29 30 30 384
+921 */{ 0 , 2 , 11 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354
+922 */{ 0 , 1 , 31 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+923 */{ 4 , 1 , 20 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384
+924 */{ 0 , 2 , 8 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354
+925 */{ 12 , 1 , 27 , 44384 },/* 30 29 30 29 30 30 29 30 29 30 30 29 29 384
+926 */{ 0 , 2 , 15 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+927 */{ 0 , 2 , 5 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355
+928 */{ 8 , 1 , 26 , 17848 },/* 29 30 29 29 29 30 29 30 30 29 30 30 30 384
+929 */{ 0 , 2 , 13 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354
+930 */{ 0 , 2 , 2 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+931 */{ 5 , 1 , 22 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 29 383
+932 */{ 0 , 2 , 9 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355
+933 */{ 0 , 1 , 29 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+934 */{ 1 , 1 , 18 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+935 */{ 0 , 2 , 6 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+936 */{ 11 , 1 , 27 , 21344 },/* 29 30 29 30 29 29 30 30 29 30 30 29 29 383
+937 */{ 0 , 2 , 13 , 51904 },/* 30 30 29 29 30 29 30 29 30 30 29 29 0 354
+938 */{ 0 , 2 , 2 , 58720 },/* 30 30 30 29 29 30 29 30 29 30 30 29 0 355
+939 */{ 7 , 1 , 23 , 53928 },/* 30 30 29 30 29 29 30 29 30 29 30 29 30 384
+940 */{ 0 , 2 , 11 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354
+941 */{ 0 , 1 , 30 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355
+942 */{ 3 , 1 , 20 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+943 */{ 0 , 2 , 8 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+944 */{ 12 , 1 , 28 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 29 384
+945 */{ 0 , 2 , 15 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355
+946 */{ 0 , 2 , 5 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354
+947 */{ 7 , 1 , 25 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+948 */{ 0 , 2 , 13 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+949 */{ 0 , 2 , 1 , 45664 },/* 30 29 30 30 29 29 30 29 29 30 30 29 0 354
+950 */{ 5 , 1 , 21 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+951 */{ 0 , 2 , 9 , 45936 },/* 30 29 30 30 29 30 29 30 29 30 29 0 0 325
+952 */{ 0 , 12 , 31 , 43728 },/* 30 29 30 29 30 29 30 29 30 30 29 30 29 384
+953 */{ 1 , 1 , 18 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384
+954 */{ 0 , 2 , 6 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+955 */{ 9 , 1 , 27 , 19128 },/* 29 30 29 29 30 29 30 29 30 29 30 30 30 384
+956 */{ 0 , 2 , 15 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+957 */{ 0 , 2 , 3 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+958 */{ 7 , 1 , 23 , 43672 },/* 30 29 30 29 30 29 30 29 30 29 29 30 30 384
+959 */{ 0 , 2 , 11 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+960 */{ 0 , 1 , 31 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+961 */{ 3 , 1 , 20 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384
+962 */{ 0 , 2 , 8 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+963 */{ 12 , 1 , 28 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384
+964 */{ 0 , 2 , 16 , 41840 },/* 30 29 30 29 29 29 30 30 29 30 30 30 0 355
+965 */{ 0 , 2 , 5 , 20848 },/* 29 30 29 30 29 29 29 30 29 30 30 30 0 354
+966 */{ 8 , 1 , 25 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383
+967 */{ 0 , 2 , 12 , 54448 },/* 30 30 29 30 29 30 29 29 30 29 30 30 0 355
+968 */{ 0 , 2 , 2 , 23184 },/* 29 30 29 30 30 29 30 29 30 29 29 30 0 354
+969 */{ 5 , 1 , 21 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384
+970 */{ 0 , 2 , 9 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+971 */{ 0 , 1 , 30 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354
+972 */{ 2 , 1 , 19 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384
+973 */{ 0 , 2 , 6 , 41696 },/* 30 29 30 29 29 29 30 29 30 30 30 29 0 354
+974 */{ 10 , 1 , 26 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384
+975 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+976 */{ 0 , 2 , 3 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+977 */{ 7 , 1 , 22 , 55952 },/* 30 30 29 30 30 29 30 29 30 29 29 30 29 384
+978 */{ 0 , 2 , 10 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355
+979 */{ 0 , 1 , 31 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+980 */{ 3 , 1 , 21 , 10968 },/* 29 29 30 29 30 29 30 29 30 30 29 30 30 384
+981 */{ 0 , 2 , 8 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+982 */{ 12 , 1 , 28 , 37592 },/* 30 29 29 30 29 29 30 29 30 30 29 30 30 384
+983 */{ 0 , 2 , 16 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354
+984 */{ 0 , 2 , 5 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+985 */{ 9 , 1 , 24 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384
+986 */{ 0 , 2 , 12 , 44192 },/* 30 29 30 29 30 30 29 29 30 29 30 29 0 354
+987 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+988 */{ 5 , 1 , 22 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384
+989 */{ 0 , 2 , 9 , 19376 },/* 29 30 29 29 30 29 30 30 30 29 30 30 0 355
+990 */{ 0 , 1 , 30 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 0 354
+991 */{ 2 , 1 , 19 , 37560 },/* 30 29 29 30 29 29 30 29 30 29 30 30 30 384
+992 */{ 0 , 2 , 7 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+993 */{ 10 , 1 , 26 , 26968 },/* 29 30 30 29 30 29 29 30 29 30 29 30 30 384
+994 */{ 0 , 2 , 14 , 22864 },/* 29 30 29 30 30 29 29 30 29 30 29 30 0 354
+995 */{ 0 , 2 , 3 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+996 */{ 7 , 1 , 23 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384
+997 */{ 0 , 2 , 10 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+998 */{ 0 , 1 , 31 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+999 */{ 3 , 1 , 20 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1000 */{ 0 , 2 , 8 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354
+1001 */{ 12 , 1 , 28 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 29 383
+1002 */{ 0 , 2 , 15 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354
+1003 */{ 0 , 2 , 4 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1004 */{ 9 , 1 , 25 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+1005 */{ 0 , 2 , 12 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1006 */{ 0 , 2 , 1 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+1007 */{ 5 , 1 , 22 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1008 */{ 0 , 2 , 10 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354
+1009 */{ 0 , 1 , 29 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1010 */{ 2 , 1 , 18 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+1011 */{ 0 , 2 , 6 , 45664 },/* 30 29 30 30 29 29 30 29 29 30 30 29 0 354
+1012 */{ 10 , 1 , 26 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1013 */{ 0 , 2 , 13 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+1014 */{ 0 , 2 , 3 , 13728 },/* 29 29 30 30 29 30 29 30 30 29 30 29 0 354
+1015 */{ 6 , 1 , 23 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384
+1016 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1017 */{ 0 , 1 , 31 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1018 */{ 4 , 1 , 20 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+1019 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1020 */{ 12 , 1 , 28 , 43608 },/* 30 29 30 29 30 29 30 29 29 30 29 30 30 384
+1021 */{ 0 , 2 , 15 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1022 */{ 0 , 2 , 4 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+1023 */{ 9 , 1 , 25 , 11688 },/* 29 29 30 29 30 30 29 30 30 29 30 29 30 384
+1024 */{ 0 , 2 , 13 , 11088 },/* 29 29 30 29 30 29 30 30 29 30 29 30 0 354
+1025 */{ 0 , 2 , 1 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355
+1026 */{ 5 , 1 , 22 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 29 383
+1027 */{ 0 , 2 , 9 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 0 355
+1028 */{ 0 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1029 */{ 2 , 1 , 18 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1030 */{ 0 , 2 , 5 , 55952 },/* 30 30 29 30 30 29 30 29 30 29 29 30 0 355
+1031 */{ 10 , 1 , 26 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384
+1032 */{ 0 , 2 , 14 , 26320 },/* 29 30 30 29 29 30 30 29 30 30 29 30 0 355
+1033 */{ 0 , 2 , 3 , 9952 },/* 29 29 30 29 29 30 30 29 30 30 30 29 0 354
+1034 */{ 6 , 1 , 23 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384
+1035 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1036 */{ 0 , 1 , 31 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+1037 */{ 4 , 1 , 19 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1038 */{ 0 , 2 , 7 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1039 */{ 12 , 1 , 27 , 54928 },/* 30 30 29 30 29 30 30 29 30 29 29 30 29 384
+1040 */{ 0 , 2 , 15 , 46464 },/* 30 29 30 30 29 30 29 30 30 29 29 29 0 354
+1041 */{ 0 , 2 , 3 , 54960 },/* 30 30 29 30 29 30 30 29 30 29 30 30 0 356
+1042 */{ 9 , 1 , 25 , 9944 },/* 29 29 30 29 29 30 30 29 30 30 29 30 30 384
+1043 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1044 */{ 0 , 2 , 2 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354
+1045 */{ 5 , 1 , 21 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1046 */{ 0 , 2 , 9 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1047 */{ 0 , 1 , 29 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+1048 */{ 1 , 1 , 18 , 46424 },/* 30 29 30 30 29 30 29 30 29 30 29 30 30 385
+1049 */{ 0 , 2 , 6 , 11600 },/* 29 29 30 29 30 30 29 30 29 30 29 30 0 354
+1050 */{ 11 , 1 , 26 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384
+1051 */{ 0 , 2 , 14 , 19376 },/* 29 30 29 29 30 29 30 30 30 29 30 30 0 355
+1052 */{ 0 , 2 , 4 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 0 354
+1053 */{ 7 , 1 , 23 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+1054 */{ 0 , 2 , 11 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1055 */{ 0 , 1 , 31 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354
+1056 */{ 3 , 1 , 20 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384
+1057 */{ 0 , 2 , 7 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1058 */{ 12 , 1 , 27 , 43864 },/* 30 29 30 29 30 29 30 30 29 30 29 30 30 385
+1059 */{ 0 , 2 , 16 , 10064 },/* 29 29 30 29 29 30 30 30 29 30 29 30 0 354
+1060 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1061 */{ 8 , 1 , 24 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1062 */{ 0 , 2 , 12 , 42336 },/* 30 29 30 29 29 30 29 30 29 30 30 29 0 354
+1063 */{ 0 , 2 , 1 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+1064 */{ 5 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+1065 */{ 0 , 2 , 8 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1066 */{ 0 , 1 , 29 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1067 */{ 1 , 1 , 18 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384
+1068 */{ 0 , 2 , 6 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+1069 */{ 11 , 1 , 26 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1070 */{ 0 , 2 , 14 , 18896 },/* 29 30 29 29 30 29 29 30 30 30 29 30 0 354
+1071 */{ 0 , 2 , 3 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1072 */{ 7 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+1073 */{ 0 , 2 , 10 , 43616 },/* 30 29 30 29 30 29 30 29 29 30 30 29 0 354
+1074 */{ 0 , 1 , 30 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1075 */{ 4 , 1 , 20 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1076 */{ 0 , 2 , 8 , 13728 },/* 29 29 30 30 29 30 29 30 30 29 30 29 0 354
+1077 */{ 0 , 1 , 27 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355
+1078 */{ 1 , 1 , 17 , 19352 },/* 29 30 29 29 30 29 30 30 30 29 29 30 30 384
+1079 */{ 0 , 2 , 5 , 17840 },/* 29 30 29 29 29 30 29 30 30 29 30 30 0 354
+1080 */{ 9 , 1 , 25 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+1081 */{ 0 , 2 , 12 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1082 */{ 0 , 2 , 1 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+1083 */{ 6 , 1 , 21 , 46408 },/* 30 29 30 30 29 30 29 30 29 30 29 29 30 384
+1084 */{ 0 , 2 , 9 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 0 355
+1085 */{ 0 , 1 , 29 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354
+1086 */{ 2 , 1 , 18 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384
+1087 */{ 0 , 2 , 6 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355
+1088 */{ 12 , 1 , 27 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 29 383
+1089 */{ 0 , 2 , 13 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 0 355
+1090 */{ 0 , 2 , 3 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1091 */{ 8 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1092 */{ 0 , 2 , 10 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+1093 */{ 0 , 1 , 30 , 23360 },/* 29 30 29 30 30 29 30 30 29 30 29 29 0 354
+1094 */{ 4 , 1 , 19 , 43880 },/* 30 29 30 29 30 29 30 30 29 30 30 29 30 385
+1095 */{ 0 , 2 , 8 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354
+1096 */{ 0 , 1 , 28 , 58896 },/* 30 30 30 29 29 30 30 29 29 29 29 30 0 354
+1097 */{ 2 , 1 , 16 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384
+1098 */{ 0 , 2 , 4 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+1099 */{ 9 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1100 */{ 0 , 2 , 12 , 21664 },/* 29 30 29 30 29 30 29 29 30 29 30 29 0 353
+1101 */{ 0 , 1 , 31 , 54864 },/* 30 30 29 30 29 30 30 29 29 30 29 30 0 355
+1102 */{ 6 , 1 , 21 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+1103 */{ 0 , 2 , 9 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355
+1104 */{ 0 , 1 , 30 , 9936 },/* 29 29 30 29 29 30 30 29 30 30 29 30 0 354
+1105 */{ 2 , 1 , 18 , 37608 },/* 30 29 29 30 29 29 30 29 30 30 30 29 30 384
+1106 */{ 0 , 2 , 6 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354
+1107 */{ 10 , 1 , 26 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1108 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1109 */{ 0 , 2 , 2 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+1110 */{ 8 , 1 , 22 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1111 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+1112 */{ 0 , 1 , 31 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355
+1113 */{ 4 , 1 , 20 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384
+1114 */{ 0 , 2 , 8 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354
+1115 */{ 0 , 1 , 28 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1116 */{ 1 , 1 , 17 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1117 */{ 0 , 2 , 4 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354
+1118 */{ 9 , 1 , 24 , 29352 },/* 29 30 30 30 29 29 30 29 30 29 30 29 30 384
+1119 */{ 0 , 2 , 12 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1120 */{ 0 , 2 , 1 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1121 */{ 5 , 1 , 21 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384
+1122 */{ 0 , 2 , 9 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1123 */{ 0 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1124 */{ 3 , 1 , 19 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383
+1125 */{ 0 , 2 , 5 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+1126 */{ 11 , 1 , 25 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+1127 */{ 0 , 2 , 13 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1128 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1129 */{ 8 , 1 , 22 , 39824 },/* 30 29 29 30 30 29 30 30 30 29 29 30 29 384
+1130 */{ 0 , 2 , 10 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+1131 */{ 0 , 1 , 31 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1132 */{ 4 , 1 , 20 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384
+1133 */{ 0 , 2 , 7 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1134 */{ 0 , 1 , 27 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1135 */{ 2 , 1 , 16 , 55592 },/* 30 30 29 30 30 29 29 30 29 29 30 29 30 384
+1136 */{ 0 , 2 , 4 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354
+1137 */{ 10 , 1 , 23 , 54952 },/* 30 30 29 30 29 30 30 29 30 29 30 29 30 385
+1138 */{ 0 , 2 , 12 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354
+1139 */{ 0 , 2 , 1 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355
+1140 */{ 6 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384
+1141 */{ 0 , 2 , 9 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1142 */{ 0 , 1 , 29 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1143 */{ 4 , 1 , 18 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1144 */{ 0 , 2 , 6 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1145 */{ 11 , 1 , 25 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+1146 */{ 0 , 2 , 13 , 27456 },/* 29 30 30 29 30 29 30 30 29 30 29 29 0 354
+1147 */{ 0 , 2 , 2 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1148 */{ 8 , 1 , 23 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384
+1149 */{ 0 , 2 , 10 , 39280 },/* 30 29 29 30 30 29 29 30 29 30 30 30 0 355
+1150 */{ 0 , 1 , 31 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1151 */{ 4 , 1 , 20 , 25784 },/* 29 30 30 29 29 30 29 29 30 29 30 30 30 384
+1152 */{ 0 , 2 , 8 , 21680 },/* 29 30 29 30 29 30 29 29 30 29 30 30 0 354
+1153 */{ 12 , 1 , 27 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1154 */{ 0 , 2 , 14 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+1155 */{ 0 , 2 , 4 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354
+1156 */{ 10 , 1 , 24 , 43880 },/* 30 29 30 29 30 29 30 30 29 30 30 29 30 385
+1157 */{ 0 , 2 , 12 , 9952 },/* 29 29 30 29 29 30 30 29 30 30 30 29 0 354
+1158 */{ 0 , 2 , 1 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1159 */{ 6 , 1 , 21 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384
+1160 */{ 0 , 2 , 9 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+1161 */{ 0 , 1 , 28 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1162 */{ 2 , 1 , 17 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384
+1163 */{ 0 , 2 , 5 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1164 */{ 11 , 1 , 26 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1165 */{ 0 , 2 , 13 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355
+1166 */{ 0 , 2 , 3 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1167 */{ 7 , 1 , 23 , 37608 },/* 30 29 29 30 29 29 30 29 30 30 30 29 30 384
+1168 */{ 0 , 2 , 11 , 37488 },/* 30 29 29 30 29 29 30 29 29 30 30 30 0 354
+1169 */{ 0 , 1 , 30 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1170 */{ 5 , 1 , 19 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1171 */{ 0 , 2 , 7 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+1172 */{ 0 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1173 */{ 1 , 1 , 16 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1174 */{ 0 , 2 , 4 , 19888 },/* 29 30 29 29 30 30 29 30 30 29 30 30 0 355
+1175 */{ 9 , 1 , 25 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 29 383
+1176 */{ 0 , 2 , 12 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1177 */{ 0 , 2 , 1 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1178 */{ 6 , 1 , 21 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1179 */{ 0 , 2 , 9 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354
+1180 */{ 0 , 1 , 29 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1181 */{ 3 , 1 , 17 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1182 */{ 0 , 2 , 5 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1183 */{ 11 , 1 , 26 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384
+1184 */{ 0 , 2 , 14 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1185 */{ 0 , 2 , 2 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1186 */{ 7 , 1 , 23 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383
+1187 */{ 0 , 2 , 10 , 53392 },/* 30 30 29 30 29 29 30 29 29 30 30 0 0 325
+1188 */{ 0 , 1 , 1 , 29848 },/* 29 30 30 30 29 30 29 29 30 29 29 30 30 384
+1189 */{ 5 , 1 , 19 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384
+1190 */{ 0 , 2 , 7 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1191 */{ 0 , 1 , 27 , 39760 },/* 30 29 29 30 30 29 30 30 29 30 29 30 0 355
+1192 */{ 2 , 1 , 17 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384
+1193 */{ 0 , 2 , 4 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1194 */{ 10 , 1 , 24 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384
+1195 */{ 0 , 2 , 12 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1196 */{ 0 , 2 , 1 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+1197 */{ 6 , 1 , 20 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384
+1198 */{ 0 , 2 , 8 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1199 */{ 0 , 1 , 28 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355
+1200 */{ 2 , 1 , 18 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384
+1201 */{ 0 , 2 , 5 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355
+1202 */{ 12 , 1 , 26 , 18904 },/* 29 30 29 29 30 29 29 30 30 30 29 30 30 384
+1203 */{ 0 , 2 , 14 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1204 */{ 0 , 2 , 3 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1205 */{ 8 , 1 , 22 , 43608 },/* 30 29 30 29 30 29 30 29 29 30 29 30 30 384
+1206 */{ 0 , 2 , 10 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1207 */{ 0 , 1 , 30 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354
+1208 */{ 4 , 1 , 19 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 29 384
+1209 */{ 0 , 2 , 6 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1210 */{ 0 , 1 , 27 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1211 */{ 2 , 1 , 17 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384
+1212 */{ 0 , 2 , 5 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1213 */{ 9 , 1 , 24 , 25784 },/* 29 30 30 29 29 30 29 29 30 29 30 30 30 384
+1214 */{ 0 , 2 , 12 , 21680 },/* 29 30 29 30 29 30 29 29 30 29 30 30 0 354
+1215 */{ 0 , 2 , 1 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1216 */{ 7 , 1 , 21 , 27944 },/* 29 30 30 29 30 30 29 30 29 29 30 29 30 384
+1217 */{ 0 , 2 , 8 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354
+1218 */{ 0 , 1 , 28 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1219 */{ 3 , 1 , 18 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384
+1220 */{ 0 , 2 , 6 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1221 */{ 12 , 1 , 25 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384
+1222 */{ 0 , 2 , 13 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+1223 */{ 0 , 2 , 2 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1224 */{ 8 , 1 , 22 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384
+1225 */{ 0 , 2 , 9 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1226 */{ 0 , 1 , 30 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1227 */{ 5 , 1 , 19 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385
+1228 */{ 0 , 2 , 8 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1229 */{ 0 , 1 , 27 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1230 */{ 2 , 1 , 16 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384
+1231 */{ 0 , 2 , 4 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1232 */{ 9 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1233 */{ 0 , 2 , 11 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354
+1234 */{ 0 , 1 , 31 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1235 */{ 7 , 1 , 21 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1236 */{ 0 , 2 , 9 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354
+1237 */{ 0 , 1 , 28 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+1238 */{ 4 , 1 , 18 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+1239 */{ 0 , 2 , 6 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1240 */{ 12 , 1 , 26 , 43320 },/* 30 29 30 29 30 29 29 30 29 29 30 30 30 384
+1241 */{ 0 , 2 , 13 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354
+1242 */{ 0 , 2 , 2 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1243 */{ 8 , 1 , 22 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1244 */{ 0 , 2 , 10 , 44624 },/* 30 29 30 29 30 30 30 29 29 30 29 30 0 355
+1245 */{ 0 , 1 , 30 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1246 */{ 4 , 1 , 19 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1247 */{ 0 , 2 , 7 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1248 */{ 0 , 1 , 28 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354
+1249 */{ 2 , 1 , 16 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383
+1250 */{ 0 , 2 , 3 , 58672 },/* 30 30 30 29 29 30 29 30 29 29 30 30 0 355
+1251 */{ 10 , 1 , 24 , 27800 },/* 29 30 30 29 30 30 29 29 30 29 29 30 30 384
+1252 */{ 0 , 2 , 12 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1253 */{ 0 , 1 , 31 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355
+1254 */{ 6 , 1 , 21 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384
+1255 */{ 0 , 2 , 9 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1256 */{ 0 , 1 , 29 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354
+1257 */{ 4 , 1 , 17 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+1258 */{ 0 , 2 , 5 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+1259 */{ 11 , 1 , 25 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384
+1260 */{ 0 , 2 , 13 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1261 */{ 0 , 2 , 1 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355
+1262 */{ 9 , 1 , 22 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384
+1263 */{ 0 , 2 , 10 , 21872 },/* 29 30 29 30 29 30 29 30 29 30 30 30 0 355
+1264 */{ 0 , 1 , 31 , 18896 },/* 29 30 29 29 30 29 29 30 30 30 29 30 0 354
+1265 */{ 5 , 1 , 19 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+1266 */{ 0 , 2 , 7 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1267 */{ 0 , 1 , 27 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+1268 */{ 1 , 1 , 16 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+1269 */{ 0 , 2 , 3 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1270 */{ 11 , 1 , 23 , 46528 },/* 30 29 30 30 29 30 29 30 30 30 29 29 29 384
+1271 */{ 0 , 2 , 11 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1272 */{ 0 , 2 , 1 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1273 */{ 6 , 1 , 21 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384
+1274 */{ 0 , 2 , 9 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1275 */{ 0 , 1 , 29 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1276 */{ 3 , 1 , 18 , 27224 },/* 29 30 30 29 30 29 30 29 29 30 29 30 30 384
+1277 */{ 0 , 2 , 5 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1278 */{ 11 , 1 , 25 , 27432 },/* 29 30 30 29 30 29 30 30 29 29 30 29 30 384
+1279 */{ 0 , 2 , 13 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354
+1280 */{ 0 , 2 , 2 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1281 */{ 8 , 1 , 22 , 10984 },/* 29 29 30 29 30 29 30 29 30 30 30 29 30 384
+1282 */{ 0 , 2 , 10 , 18912 },/* 29 30 29 29 30 29 29 30 30 30 30 29 0 354
+1283 */{ 0 , 1 , 30 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1284 */{ 5 , 1 , 19 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384
+1285 */{ 0 , 2 , 6 , 45648 },/* 30 29 30 30 29 29 30 29 29 30 29 30 0 354
+1286 */{ 0 , 1 , 26 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1287 */{ 2 , 1 , 15 , 62096 },/* 30 30 30 30 29 29 30 29 30 29 29 30 29 384
+1288 */{ 0 , 2 , 3 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355
+1289 */{ 10 , 1 , 23 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384
+1290 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1291 */{ 0 , 2 , 1 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1292 */{ 6 , 1 , 21 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+1293 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1294 */{ 0 , 1 , 28 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+1295 */{ 4 , 1 , 17 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+1296 */{ 0 , 2 , 5 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+1297 */{ 12 , 1 , 24 , 44384 },/* 30 29 30 29 30 30 29 30 29 30 30 29 29 384
+1298 */{ 0 , 2 , 12 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1299 */{ 0 , 2 , 2 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355
+1300 */{ 8 , 1 , 23 , 2424 },/* 29 29 29 29 30 29 29 30 29 30 30 30 30 383
+1301 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1302 */{ 0 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1303 */{ 5 , 1 , 19 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1304 */{ 0 , 2 , 6 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+1305 */{ 0 , 1 , 26 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1306 */{ 1 , 1 , 15 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384
+1307 */{ 0 , 2 , 3 , 42720 },/* 30 29 30 29 29 30 30 29 30 30 30 29 0 355
+1308 */{ 11 , 1 , 24 , 37608 },/* 30 29 29 30 29 29 30 29 30 30 30 29 30 384
+1309 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1310 */{ 0 , 1 , 31 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+1311 */{ 7 , 1 , 20 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1312 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1313 */{ 0 , 1 , 27 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1314 */{ 3 , 1 , 17 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+1315 */{ 0 , 2 , 5 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1316 */{ 0 , 1 , 25 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355
+1317 */{ 1 , 1 , 14 , 37608 },/* 30 29 29 30 29 29 30 29 30 30 30 29 30 384
+1318 */{ 0 , 2 , 2 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354
+1319 */{ 8 , 1 , 22 , 42328 },/* 30 29 30 29 29 30 29 30 29 30 29 30 30 384
+1320 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1321 */{ 0 , 1 , 29 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354
+1322 */{ 5 , 1 , 18 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1323 */{ 0 , 2 , 6 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+1324 */{ 0 , 1 , 27 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354
+1325 */{ 1 , 1 , 15 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384
+1326 */{ 0 , 2 , 3 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1327 */{ 9 , 1 , 24 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+1328 */{ 0 , 2 , 12 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354
+1329 */{ 0 , 1 , 31 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354
+1330 */{ 7 , 1 , 20 , 27288 },/* 29 30 30 29 30 29 30 29 30 29 29 30 30 384
+1331 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1332 */{ 0 , 1 , 28 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1333 */{ 3 , 1 , 17 , 19368 },/* 29 30 29 29 30 29 30 30 30 29 30 29 30 384
+1334 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1335 */{ 12 , 1 , 25 , 42608 },/* 30 29 30 29 29 30 30 29 29 30 30 30 29 384
+1336 */{ 0 , 2 , 13 , 41696 },/* 30 29 30 29 29 29 30 29 30 30 30 29 0 354
+1337 */{ 0 , 2 , 1 , 53600 },/* 30 30 29 30 29 29 29 30 29 30 30 29 0 354
+1338 */{ 8 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+1339 */{ 0 , 2 , 9 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1340 */{ 0 , 1 , 29 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355
+1341 */{ 5 , 1 , 18 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 29 384
+1342 */{ 0 , 2 , 6 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+1343 */{ 0 , 1 , 27 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1344 */{ 2 , 1 , 16 , 41704 },/* 30 29 30 29 29 29 30 29 30 30 30 29 30 384
+1345 */{ 0 , 2 , 3 , 41680 },/* 30 29 30 29 29 29 30 29 30 30 29 30 0 354
+1346 */{ 10 , 1 , 23 , 53592 },/* 30 30 29 30 29 29 29 30 29 30 29 30 30 384
+1347 */{ 0 , 2 , 11 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+1348 */{ 0 , 1 , 31 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1349 */{ 7 , 1 , 19 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 29 384
+1350 */{ 0 , 2 , 7 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355
+1351 */{ 0 , 1 , 28 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355
+1352 */{ 3 , 1 , 18 , 18904 },/* 29 30 29 29 30 29 29 30 30 30 29 30 30 384
+1353 */{ 0 , 2 , 5 , 17840 },/* 29 30 29 29 29 30 29 30 30 29 30 30 0 354
+1354 */{ 0 , 1 , 25 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354
+1355 */{ 1 , 1 , 14 , 53592 },/* 30 30 29 30 29 29 29 30 29 30 29 30 30 384
+1356 */{ 0 , 2 , 2 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+1357 */{ 9 , 1 , 21 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+1358 */{ 0 , 2 , 9 , 27424 },/* 29 30 30 29 30 29 30 30 29 29 30 29 0 354
+1359 */{ 0 , 1 , 29 , 44384 },/* 30 29 30 29 30 30 29 30 29 30 30 29 0 355
+1360 */{ 5 , 1 , 19 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384
+1361 */{ 0 , 2 , 6 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355
+1362 */{ 0 , 1 , 27 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354
+1363 */{ 3 , 1 , 16 , 41656 },/* 30 29 30 29 29 29 30 29 30 29 30 30 30 384
+1364 */{ 0 , 2 , 4 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1365 */{ 10 , 1 , 23 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 29 383
+1366 */{ 0 , 2 , 10 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355
+1367 */{ 0 , 1 , 31 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1368 */{ 7 , 1 , 20 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384
+1369 */{ 0 , 2 , 7 , 42720 },/* 30 29 30 29 29 30 30 29 30 30 30 29 0 355
+1370 */{ 0 , 1 , 28 , 21216 },/* 29 30 29 30 29 29 30 29 30 30 30 29 0 354
+1371 */{ 3 , 1 , 17 , 50544 },/* 30 30 29 29 29 30 29 30 29 30 30 30 29 384
+1372 */{ 0 , 2 , 5 , 42336 },/* 30 29 30 29 29 30 29 30 29 30 30 29 0 354
+1373 */{ 11 , 1 , 24 , 53928 },/* 30 30 29 30 29 29 30 29 30 29 30 29 30 384
+1374 */{ 0 , 2 , 12 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354
+1375 */{ 0 , 2 , 1 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1376 */{ 9 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+1377 */{ 0 , 2 , 9 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1378 */{ 0 , 1 , 29 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355
+1379 */{ 5 , 1 , 19 , 21224 },/* 29 30 29 30 29 29 30 29 30 30 30 29 30 384
+1380 */{ 0 , 2 , 7 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1381 */{ 0 , 1 , 26 , 43216 },/* 30 29 30 29 30 29 29 29 30 30 29 30 0 354
+1382 */{ 2 , 1 , 15 , 53928 },/* 30 30 29 30 29 29 30 29 30 29 30 29 30 384
+1383 */{ 0 , 2 , 3 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354
+1384 */{ 10 , 1 , 23 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1385 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+1386 */{ 0 , 1 , 31 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354
+1387 */{ 6 , 1 , 20 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384
+1388 */{ 0 , 2 , 8 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1389 */{ 0 , 1 , 28 , 20912 },/* 29 30 29 30 29 29 29 30 30 29 30 30 0 354
+1390 */{ 4 , 1 , 17 , 43192 },/* 30 29 30 29 30 29 29 29 30 29 30 30 30 384
+1391 */{ 0 , 2 , 5 , 25904 },/* 29 30 30 29 29 30 29 30 29 29 30 30 0 354
+1392 */{ 12 , 1 , 25 , 27288 },/* 29 30 30 29 30 29 30 29 30 29 29 30 30 384
+1393 */{ 0 , 2 , 12 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1394 */{ 0 , 2 , 1 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1395 */{ 9 , 1 , 22 , 11176 },/* 29 29 30 29 30 29 30 30 30 29 30 29 30 384
+1396 */{ 0 , 2 , 10 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1397 */{ 0 , 1 , 29 , 50032 },/* 30 30 29 29 29 29 30 30 29 30 30 30 0 355
+1398 */{ 5 , 1 , 19 , 20848 },/* 29 30 29 30 29 29 29 30 29 30 30 30 29 383
+1399 */{ 0 , 2 , 6 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+1400 */{ 0 , 1 , 26 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1401 */{ 3 , 1 , 15 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 29 383
+1402 */{ 0 , 2 , 2 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355
+1403 */{ 11 , 1 , 23 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 29 384
+1404 */{ 0 , 2 , 11 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+1405 */{ 0 , 1 , 31 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354
+1406 */{ 7 , 1 , 20 , 41704 },/* 30 29 30 29 29 29 30 29 30 30 30 29 30 384
+1407 */{ 0 , 2 , 8 , 41680 },/* 30 29 30 29 29 29 30 29 30 30 29 30 0 354
+1408 */{ 0 , 1 , 28 , 53584 },/* 30 30 29 30 29 29 29 30 29 30 29 30 0 354
+1409 */{ 4 , 1 , 16 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1410 */{ 0 , 2 , 4 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1411 */{ 12 , 1 , 24 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 29 384
+1412 */{ 0 , 2 , 12 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355
+1413 */{ 0 , 2 , 1 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355
+1414 */{ 9 , 1 , 22 , 9688 },/* 29 29 30 29 29 30 29 30 30 30 29 30 30 384
+1415 */{ 0 , 2 , 10 , 17840 },/* 29 30 29 29 29 30 29 30 30 29 30 30 0 354
+1416 */{ 0 , 1 , 30 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354
+1417 */{ 5 , 1 , 18 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1418 */{ 0 , 2 , 6 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1419 */{ 0 , 1 , 26 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1420 */{ 1 , 1 , 15 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1421 */{ 0 , 2 , 2 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1422 */{ 12 , 1 , 23 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384
+1423 */{ 0 , 2 , 11 , 19312 },/* 29 30 29 29 30 29 30 30 29 30 30 30 0 355
+1424 */{ 0 , 2 , 1 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354
+1425 */{ 7 , 1 , 20 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+1426 */{ 0 , 2 , 8 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1427 */{ 0 , 1 , 28 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354
+1428 */{ 4 , 1 , 17 , 27816 },/* 29 30 30 29 30 30 29 29 30 29 30 29 30 384
+1429 */{ 0 , 2 , 4 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1430 */{ 12 , 1 , 24 , 39760 },/* 30 29 29 30 30 29 30 30 29 30 29 30 29 384
+1431 */{ 0 , 2 , 12 , 42720 },/* 30 29 30 29 29 30 30 29 30 30 30 29 0 355
+1432 */{ 0 , 2 , 2 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1433 */{ 8 , 1 , 21 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1434 */{ 0 , 2 , 9 , 42336 },/* 30 29 30 29 29 30 29 30 29 30 30 29 0 354
+1435 */{ 0 , 1 , 29 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354
+1436 */{ 6 , 1 , 18 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 29 384
+1437 */{ 0 , 2 , 5 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1438 */{ 0 , 1 , 26 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1439 */{ 2 , 1 , 15 , 43728 },/* 30 29 30 29 30 29 30 29 30 30 29 30 29 384
+1440 */{ 0 , 2 , 3 , 38368 },/* 30 29 29 30 29 30 29 30 30 30 30 29 0 355
+1441 */{ 11 , 1 , 23 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1442 */{ 0 , 2 , 11 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1443 */{ 0 , 1 , 31 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1444 */{ 7 , 1 , 20 , 53872 },/* 30 30 29 30 29 29 30 29 29 30 30 30 29 384
+1445 */{ 0 , 2 , 7 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354
+1446 */{ 0 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1447 */{ 4 , 1 , 17 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1448 */{ 0 , 2 , 5 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354
+1449 */{ 0 , 1 , 24 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1450 */{ 1 , 1 , 14 , 19128 },/* 29 30 29 29 30 29 30 29 30 29 30 30 30 384
+1451 */{ 0 , 2 , 2 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1452 */{ 9 , 1 , 22 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+1453 */{ 0 , 2 , 9 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1454 */{ 0 , 1 , 29 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354
+1455 */{ 6 , 1 , 18 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1456 */{ 0 , 2 , 6 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 0 355
+1457 */{ 0 , 1 , 26 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1458 */{ 2 , 1 , 15 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384
+1459 */{ 0 , 2 , 3 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355
+1460 */{ 11 , 1 , 24 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 29 383
+1461 */{ 0 , 2 , 10 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+1462 */{ 0 , 1 , 30 , 58544 },/* 30 30 30 29 29 30 29 29 30 29 30 30 0 355
+1463 */{ 7 , 1 , 20 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 29 383
+1464 */{ 0 , 2 , 7 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355
+1465 */{ 0 , 1 , 27 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355
+1466 */{ 3 , 1 , 17 , 11112 },/* 29 29 30 29 30 29 30 30 29 30 30 29 30 384
+1467 */{ 0 , 2 , 5 , 9952 },/* 29 29 30 29 29 30 30 29 30 30 30 29 0 354
+1468 */{ 0 , 1 , 25 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1469 */{ 2 , 1 , 13 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384
+1470 */{ 0 , 2 , 1 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354
+1471 */{ 9 , 1 , 21 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1472 */{ 0 , 2 , 9 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+1473 */{ 0 , 1 , 28 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 0 355
+1474 */{ 6 , 1 , 18 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384
+1475 */{ 0 , 2 , 6 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355
+1476 */{ 0 , 1 , 27 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1477 */{ 2 , 1 , 15 , 37592 },/* 30 29 29 30 29 29 30 29 30 30 29 30 30 384
+1478 */{ 0 , 2 , 3 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354
+1479 */{ 10 , 1 , 23 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1480 */{ 0 , 2 , 11 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354
+1481 */{ 0 , 1 , 30 , 29856 },/* 29 30 30 30 29 30 29 29 30 29 30 29 0 354
+1482 */{ 8 , 1 , 19 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1483 */{ 0 , 2 , 7 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1484 */{ 0 , 1 , 28 , 21424 },/* 29 30 29 30 29 29 30 30 30 29 30 30 0 355
+1485 */{ 4 , 1 , 17 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384
+1486 */{ 0 , 2 , 5 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354
+1487 */{ 0 , 1 , 25 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1488 */{ 1 , 1 , 14 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1489 */{ 0 , 2 , 1 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354
+1490 */{ 9 , 1 , 21 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384
+1491 */{ 0 , 2 , 9 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1492 */{ 0 , 1 , 29 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1493 */{ 5 , 1 , 18 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384
+1494 */{ 0 , 2 , 6 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1495 */{ 0 , 1 , 26 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1496 */{ 3 , 1 , 16 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 29 383
+1497 */{ 0 , 2 , 2 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354
+1498 */{ 11 , 1 , 22 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 29 384
+1499 */{ 0 , 2 , 10 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1500 */{ 0 , 1 , 31 , 5792 },/* 29 29 29 30 29 30 30 29 30 29 30 29 0 353
+1501 */{ 7 , 1 , 19 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384
+1502 */{ 0 , 2 , 7 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355
+1503 */{ 0 , 1 , 28 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1504 */{ 4 , 1 , 17 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+1505 */{ 0 , 2 , 4 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1506 */{ 0 , 1 , 24 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1507 */{ 1 , 1 , 13 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 29 384
+1508 */{ 0 , 2 , 1 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1509 */{ 9 , 1 , 21 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1510 */{ 0 , 2 , 9 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354
+1511 */{ 0 , 1 , 29 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1512 */{ 5 , 1 , 19 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384
+1513 */{ 0 , 2 , 6 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1514 */{ 0 , 1 , 26 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1515 */{ 4 , 1 , 15 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1516 */{ 0 , 2 , 3 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354
+1517 */{ 12 , 1 , 22 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1518 */{ 0 , 2 , 10 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 0 355
+1519 */{ 0 , 1 , 31 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1520 */{ 8 , 1 , 20 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384
+1521 */{ 0 , 2 , 7 , 37616 },/* 30 29 29 30 29 29 30 29 30 30 30 30 0 355
+1522 */{ 0 , 1 , 28 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1523 */{ 4 , 1 , 17 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383
+1524 */{ 0 , 2 , 4 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1525 */{ 12 , 1 , 23 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 29 384
+1526 */{ 0 , 2 , 11 , 54928 },/* 30 30 29 30 29 30 30 29 30 29 29 30 0 355
+1527 */{ 0 , 2 , 1 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+1528 */{ 10 , 1 , 22 , 11112 },/* 29 29 30 29 30 29 30 30 29 30 30 29 30 384
+1529 */{ 0 , 2 , 9 , 9952 },/* 29 29 30 29 29 30 30 29 30 30 30 29 0 354
+1530 */{ 0 , 1 , 29 , 21216 },/* 29 30 29 30 29 29 30 29 30 30 30 29 0 354
+1531 */{ 6 , 1 , 18 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384
+1532 */{ 0 , 2 , 6 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354
+1533 */{ 0 , 1 , 25 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1534 */{ 2 , 1 , 14 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384
+1535 */{ 0 , 2 , 2 , 46480 },/* 30 29 30 30 29 30 29 30 30 29 29 30 0 355
+1536 */{ 12 , 1 , 23 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384
+1537 */{ 0 , 2 , 10 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355
+1538 */{ 0 , 1 , 31 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1539 */{ 7 , 1 , 20 , 37592 },/* 30 29 29 30 29 29 30 29 30 30 29 30 30 384
+1540 */{ 0 , 2 , 8 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354
+1541 */{ 0 , 1 , 27 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1542 */{ 5 , 1 , 16 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384
+1543 */{ 0 , 2 , 4 , 27808 },/* 29 30 30 29 30 30 29 29 30 29 30 29 0 354
+1544 */{ 0 , 1 , 24 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+1545 */{ 1 , 1 , 13 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384
+1546 */{ 0 , 2 , 1 , 19376 },/* 29 30 29 29 30 29 30 30 30 29 30 30 0 355
+1547 */{ 9 , 1 , 22 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384
+1548 */{ 0 , 2 , 10 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354
+1549 */{ 0 , 1 , 29 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1550 */{ 6 , 1 , 18 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383
+1551 */{ 0 , 2 , 5 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355
+1552 */{ 0 , 1 , 26 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1553 */{ 3 , 1 , 14 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1554 */{ 0 , 2 , 2 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1555 */{ 11 , 1 , 23 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384
+1556 */{ 0 , 2 , 11 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1557 */{ 0 , 1 , 30 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1558 */{ 7 , 1 , 20 , 21096 },/* 29 30 29 30 29 29 30 29 29 30 30 29 30 383
+1559 */{ 0 , 2 , 7 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+1560 */{ 0 , 1 , 27 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355
+1561 */{ 5 , 1 , 16 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+1562 */{ 0 , 2 , 4 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1563 */{ 0 , 1 , 24 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+1564 */{ 2 , 1 , 14 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1565 */{ 0 , 2 , 1 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1566 */{ 10 , 1 , 21 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+1567 */{ 0 , 2 , 9 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1568 */{ 0 , 1 , 29 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1569 */{ 6 , 1 , 17 , 54600 },/* 30 30 29 30 29 30 29 30 29 30 29 29 30 384
+1570 */{ 0 , 2 , 5 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1571 */{ 0 , 1 , 26 , 13728 },/* 29 29 30 30 29 30 29 30 30 29 30 29 0 354
+1572 */{ 2 , 1 , 15 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384
+1573 */{ 0 , 2 , 2 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1574 */{ 12 , 1 , 23 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384
+1575 */{ 0 , 2 , 11 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1576 */{ 0 , 1 , 31 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1577 */{ 8 , 1 , 19 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1578 */{ 0 , 2 , 7 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1579 */{ 0 , 1 , 27 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+1580 */{ 4 , 1 , 16 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385
+1581 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1582 */{ 0 , 1 , 24 , 39024 },/* 30 29 29 30 29 29 30 30 39 30 30 30 0 365
+1583 */{ 2 , 1 , 24 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384
+1584 */{ 0 , 2 , 12 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1585 */{ 9 , 1 , 31 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383
+1586 */{ 0 , 2 , 18 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1587 */{ 0 , 3 , 9 , 53968 },/* 30 30 29 30 29 30 29 29 30 29 30 0 0 325
+1588 */{ 6 , 1 , 28 , 27464 },/* 29 30 30 29 30 29 30 30 29 30 29 29 30 384
+1589 */{ 0 , 2 , 15 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+1590 */{ 0 , 2 , 5 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1591 */{ 3 , 1 , 25 , 37616 },/* 30 29 29 30 29 29 30 29 30 30 30 30 29 384
+1592 */{ 0 , 2 , 13 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1593 */{ 11 , 2 , 1 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384
+1594 */{ 0 , 2 , 20 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1595 */{ 0 , 2 , 9 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1596 */{ 8 , 1 , 29 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384
+1597 */{ 0 , 2 , 16 , 46288 },/* 30 29 30 30 29 30 29 29 30 30 29 30 0 355
+1598 */{ 0 , 2 , 6 , 22192 },/* 29 30 29 30 29 30 30 29 30 29 30 30 0 355
+1599 */{ 4 , 1 , 27 , 9944 },/* 29 29 30 29 29 30 30 29 30 30 29 30 30 384
+1600 */{ 0 , 2 , 15 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1601 */{ 0 , 2 , 3 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354
+1602 */{ 2 , 1 , 23 , 51608 },/* 30 30 29 29 30 29 29 30 30 29 29 30 30 384
+1603 */{ 0 , 2 , 11 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1604 */{ 9 , 1 , 31 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384
+1605 */{ 0 , 2 , 18 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1606 */{ 0 , 2 , 7 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+1607 */{ 6 , 1 , 28 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384
+1608 */{ 0 , 2 , 16 , 19376 },/* 29 30 29 29 30 29 30 30 30 29 30 30 0 355
+1609 */{ 0 , 2 , 5 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 0 354
+1610 */{ 3 , 1 , 25 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+1611 */{ 0 , 2 , 13 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1612 */{ 11 , 2 , 2 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383
+1613 */{ 0 , 2 , 19 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355
+1614 */{ 0 , 2 , 9 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1615 */{ 8 , 1 , 29 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1616 */{ 0 , 2 , 17 , 39760 },/* 30 29 29 30 30 29 30 30 29 30 29 30 0 355
+1617 */{ 0 , 2 , 6 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1618 */{ 4 , 1 , 26 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1619 */{ 0 , 2 , 14 , 42224 },/* 30 29 30 29 29 30 29 29 30 30 30 30 0 355
+1620 */{ 0 , 2 , 4 , 21088 },/* 29 30 29 30 29 29 30 29 29 30 30 29 0 353
+1621 */{ 2 , 1 , 22 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+1622 */{ 0 , 2 , 10 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1623 */{ 10 , 1 , 31 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+1624 */{ 0 , 2 , 19 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1625 */{ 0 , 2 , 7 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+1626 */{ 6 , 1 , 28 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1627 */{ 0 , 2 , 16 , 18912 },/* 29 30 29 29 30 29 29 30 30 30 30 29 0 354
+1628 */{ 0 , 2 , 5 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1629 */{ 4 , 1 , 24 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+1630 */{ 0 , 2 , 12 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1631 */{ 11 , 2 , 1 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384
+1632 */{ 0 , 2 , 20 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354
+1633 */{ 0 , 2 , 8 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355
+1634 */{ 8 , 1 , 29 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 29 384
+1635 */{ 0 , 2 , 17 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1636 */{ 0 , 2 , 7 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1637 */{ 4 , 1 , 26 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+1638 */{ 0 , 2 , 14 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1639 */{ 0 , 2 , 3 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+1640 */{ 1 , 1 , 23 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+1641 */{ 0 , 2 , 10 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+1642 */{ 11 , 1 , 30 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385
+1643 */{ 0 , 2 , 19 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1644 */{ 0 , 2 , 8 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355
+1645 */{ 6 , 1 , 28 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384
+1646 */{ 0 , 2 , 16 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1647 */{ 0 , 2 , 5 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1648 */{ 3 , 1 , 24 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1649 */{ 0 , 2 , 11 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+1650 */{ 11 , 2 , 1 , 27464 },/* 29 30 30 29 30 29 30 30 29 30 29 29 30 384
+1651 */{ 0 , 2 , 20 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+1652 */{ 0 , 2 , 10 , 11168 },/* 29 29 30 29 30 29 30 30 30 29 30 29 0 354
+1653 */{ 7 , 1 , 29 , 37616 },/* 30 29 29 30 29 29 30 29 30 30 30 30 29 384
+1654 */{ 0 , 2 , 17 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1655 */{ 0 , 2 , 6 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+1656 */{ 5 , 1 , 26 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1657 */{ 0 , 2 , 13 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1658 */{ 0 , 2 , 2 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+1659 */{ 3 , 1 , 23 , 39592 },/* 30 29 29 30 30 29 30 29 30 29 30 29 30 384
+1660 */{ 0 , 2 , 11 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1661 */{ 7 , 1 , 30 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 29 384
+1662 */{ 0 , 2 , 18 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 0 355
+1663 */{ 0 , 2 , 8 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354
+1664 */{ 6 , 1 , 28 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1665 */{ 0 , 2 , 15 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1666 */{ 0 , 2 , 4 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+1667 */{ 4 , 1 , 24 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1668 */{ 0 , 2 , 12 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+1669 */{ 0 , 2 , 1 , 21920 },/* 29 30 29 30 29 30 29 30 30 29 30 29 0 354
+1670 */{ 2 , 1 , 21 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384
+1671 */{ 0 , 2 , 9 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+1672 */{ 7 , 1 , 30 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+1673 */{ 0 , 2 , 17 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1674 */{ 0 , 2 , 6 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354
+1675 */{ 5 , 1 , 26 , 29864 },/* 29 30 30 30 29 30 29 29 30 29 30 29 30 384
+1676 */{ 0 , 2 , 14 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1677 */{ 0 , 2 , 2 , 44432 },/* 30 29 30 29 30 30 29 30 30 29 29 30 0 355
+1678 */{ 3 , 1 , 23 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384
+1679 */{ 0 , 2 , 11 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1680 */{ 8 , 1 , 31 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1681 */{ 0 , 2 , 18 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354
+1682 */{ 0 , 2 , 7 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+1683 */{ 6 , 1 , 27 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+1684 */{ 0 , 2 , 15 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354
+1685 */{ 0 , 2 , 3 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355
+1686 */{ 4 , 1 , 24 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384
+1687 */{ 0 , 2 , 12 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+1688 */{ 0 , 2 , 2 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1689 */{ 3 , 1 , 21 , 42216 },/* 30 29 30 29 29 30 29 29 30 30 30 29 30 384
+1690 */{ 0 , 2 , 9 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1691 */{ 7 , 1 , 29 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384
+1692 */{ 0 , 2 , 17 , 45136 },/* 30 29 29 32 29 29 29 29 29 30 29 30 0 354
+1693 */{ 0 , 2 , 5 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354
+1694 */{ 5 , 1 , 25 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 29 384
+1695 */{ 0 , 2 , 13 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355
+1696 */{ 0 , 2 , 3 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355
+1697 */{ 3 , 1 , 23 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384
+1698 */{ 0 , 2 , 11 , 18896 },/* 29 30 29 29 30 29 29 30 30 30 29 30 0 354
+1699 */{ 7 , 1 , 31 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+1700 */{ 0 , 2 , 19 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1701 */{ 0 , 2 , 8 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+1702 */{ 6 , 1 , 28 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+1703 */{ 0 , 2 , 16 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354
+1704 */{ 0 , 2 , 5 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355
+1705 */{ 4 , 1 , 25 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384
+1706 */{ 0 , 2 , 13 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355
+1707 */{ 0 , 2 , 3 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1708 */{ 3 , 1 , 23 , 25784 },/* 29 30 30 29 29 30 29 29 30 29 30 30 30 384
+1709 */{ 0 , 2 , 10 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1710 */{ 7 , 1 , 30 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1711 */{ 0 , 2 , 17 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+1712 */{ 0 , 2 , 7 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1713 */{ 5 , 1 , 26 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 29 384
+1714 */{ 0 , 2 , 14 , 43744 },/* 30 29 30 29 30 29 30 29 30 30 30 29 0 355
+1715 */{ 0 , 2 , 4 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1716 */{ 3 , 1 , 24 , 51568 },/* 30 30 29 29 30 29 29 30 29 30 30 30 29 384
+1717 */{ 0 , 2 , 11 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+1718 */{ 8 , 1 , 31 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1719 */{ 0 , 2 , 19 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1720 */{ 0 , 2 , 8 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1721 */{ 6 , 1 , 28 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+1722 */{ 0 , 2 , 16 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1723 */{ 0 , 2 , 5 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355
+1724 */{ 4 , 1 , 26 , 21224 },/* 29 30 29 30 29 29 30 29 30 30 30 29 30 384
+1725 */{ 0 , 2 , 13 , 21200 },/* 29 30 29 30 29 29 30 29 30 30 29 30 0 354
+1726 */{ 0 , 2 , 2 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1727 */{ 3 , 1 , 22 , 58536 },/* 30 30 30 29 29 30 29 29 30 29 30 29 30 384
+1728 */{ 0 , 2 , 10 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+1729 */{ 7 , 1 , 29 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1730 */{ 0 , 2 , 17 , 40272 },/* 30 29 29 30 30 30 29 30 29 30 29 30 0 355
+1731 */{ 0 , 2 , 7 , 21920 },/* 29 30 29 30 29 30 29 30 30 29 30 29 0 354
+1732 */{ 5 , 1 , 27 , 42448 },/* 30 29 30 29 29 30 29 30 30 30 29 30 29 384
+1733 */{ 0 , 2 , 14 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+1734 */{ 0 , 2 , 4 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1735 */{ 4 , 1 , 24 , 43192 },/* 30 29 30 29 30 29 29 29 30 29 30 30 30 384
+1736 */{ 0 , 2 , 12 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 0 354
+1737 */{ 9 , 1 , 31 , 27288 },/* 29 30 30 29 30 29 30 29 30 29 29 30 30 384
+1738 */{ 0 , 2 , 19 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1739 */{ 0 , 2 , 8 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1740 */{ 6 , 1 , 29 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384
+1741 */{ 0 , 2 , 16 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1742 */{ 0 , 2 , 5 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1743 */{ 4 , 1 , 26 , 20848 },/* 29 30 29 30 29 29 29 30 29 30 30 30 29 383
+1744 */{ 0 , 2 , 13 , 53600 },/* 30 30 29 30 29 29 29 30 29 30 30 29 0 354
+1745 */{ 0 , 2 , 1 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 0 355
+1746 */{ 3 , 1 , 22 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 29 383
+1747 */{ 0 , 2 , 9 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 0 355
+1748 */{ 7 , 1 , 30 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 29 384
+1749 */{ 0 , 2 , 17 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+1750 */{ 0 , 2 , 7 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1751 */{ 5 , 1 , 27 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+1752 */{ 0 , 2 , 15 , 41680 },/* 30 29 30 29 29 29 30 29 30 30 29 30 0 354
+1753 */{ 0 , 2 , 3 , 53592 },/* 30 30 29 30 29 29 29 30 29 30 29 30 30 384
+1754 */{ 4 , 2 , 22 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+1755 */{ 0 , 2 , 11 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1756 */{ 9 , 1 , 31 , 54928 },/* 30 30 29 30 29 30 30 29 30 29 29 30 29 384
+1757 */{ 0 , 2 , 18 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355
+1758 */{ 0 , 2 , 8 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355
+1759 */{ 6 , 1 , 29 , 10968 },/* 29 29 30 29 30 29 30 29 30 30 29 30 30 384
+1760 */{ 0 , 2 , 17 , 17840 },/* 29 30 29 29 29 30 29 30 30 29 30 30 0 354
+1761 */{ 0 , 2 , 5 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354
+1762 */{ 5 , 1 , 25 , 45400 },/* 30 29 30 30 29 29 29 30 29 30 29 30 30 384
+1763 */{ 0 , 2 , 13 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1764 */{ 0 , 2 , 2 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1765 */{ 2 , 1 , 21 , 46480 },/* 30 29 30 30 29 30 29 30 30 29 29 30 29 384
+1766 */{ 0 , 2 , 9 , 44384 },/* 30 29 30 29 30 30 29 30 29 30 30 29 0 355
+1767 */{ 7 , 1 , 30 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384
+1768 */{ 0 , 2 , 18 , 21360 },/* 29 30 29 30 29 29 30 30 29 30 30 30 0 355
+1769 */{ 0 , 2 , 7 , 17776 },/* 29 30 29 29 29 30 29 30 29 30 30 30 0 354
+1770 */{ 5 , 1 , 27 , 25272 },/* 29 30 30 29 29 29 30 29 30 29 30 30 30 384
+1771 */{ 0 , 2 , 15 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1772 */{ 0 , 2 , 4 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354
+1773 */{ 3 , 1 , 23 , 27816 },/* 29 30 30 29 30 30 29 29 30 29 30 29 30 384
+1774 */{ 0 , 2 , 11 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1775 */{ 10 , 1 , 31 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384
+1776 */{ 0 , 2 , 19 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355
+1777 */{ 0 , 2 , 8 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1778 */{ 6 , 1 , 28 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384
+1779 */{ 0 , 2 , 16 , 42336 },/* 30 29 30 29 29 30 29 30 29 30 30 29 0 354
+1780 */{ 0 , 2 , 5 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354
+1781 */{ 5 , 1 , 24 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 29 384
+1782 */{ 0 , 2 , 12 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1783 */{ 0 , 2 , 2 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1784 */{ 3 , 1 , 22 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384
+1785 */{ 0 , 2 , 9 , 42704 },/* 30 29 30 29 29 30 30 29 30 30 29 30 0 355
+1786 */{ 7 , 1 , 30 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1787 */{ 0 , 2 , 18 , 19120 },/* 29 30 29 29 30 29 30 29 30 29 30 30 0 354
+1788 */{ 0 , 2 , 7 , 43216 },/* 30 29 30 29 30 29 29 29 30 30 29 30 0 354
+1789 */{ 5 , 1 , 26 , 53928 },/* 30 30 29 30 29 29 30 29 30 29 30 29 30 384
+1790 */{ 0 , 2 , 14 , 45728 },/* 30 29 30 30 29 29 30 29 30 29 30 29 0 354
+1791 */{ 0 , 2 , 3 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1792 */{ 4 , 1 , 24 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1793 */{ 0 , 2 , 11 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354
+1794 */{ 0 , 1 , 31 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355
+1795 */{ 2 , 1 , 21 , 19128 },/* 29 30 29 29 30 29 30 29 30 29 30 30 30 384
+1796 */{ 0 , 2 , 9 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1797 */{ 6 , 1 , 28 , 43192 },/* 30 29 30 29 30 29 29 29 30 29 30 30 30 384
+1798 */{ 0 , 2 , 16 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1799 */{ 0 , 2 , 5 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354
+1800 */{ 4 , 1 , 25 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1801 */{ 0 , 2 , 13 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 0 355
+1802 */{ 0 , 2 , 3 , 11168 },/* 29 29 30 29 30 29 30 30 30 29 30 29 0 354
+1803 */{ 2 , 1 , 23 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384
+1804 */{ 0 , 2 , 11 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355
+1805 */{ 6 , 1 , 31 , 20848 },/* 29 30 29 30 29 29 29 30 29 30 30 30 29 383
+1806 */{ 0 , 2 , 18 , 53600 },/* 30 30 29 30 29 29 29 30 29 30 30 29 0 354
+1807 */{ 0 , 2 , 7 , 58544 },/* 30 30 30 29 29 30 29 29 30 29 30 30 0 355
+1808 */{ 5 , 1 , 28 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 29 383
+1809 */{ 0 , 2 , 14 , 55952 },/* 30 30 29 30 30 29 30 29 30 29 29 30 0 355
+1810 */{ 0 , 2 , 4 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 0 355
+1811 */{ 3 , 1 , 25 , 11112 },/* 29 29 30 29 30 29 30 30 29 30 30 29 30 384
+1812 */{ 0 , 2 , 13 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354
+1813 */{ 0 , 2 , 1 , 41696 },/* 30 29 30 29 29 29 30 29 30 30 30 29 0 354
+1814 */{ 2 , 1 , 21 , 53608 },/* 30 30 29 30 29 29 29 30 29 30 30 29 30 384
+1815 */{ 0 , 2 , 9 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354
+1816 */{ 6 , 1 , 29 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1817 */{ 0 , 2 , 16 , 46368 },/* 30 29 30 30 29 30 29 30 29 29 30 29 0 354
+1818 */{ 0 , 2 , 5 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 0 355
+1819 */{ 4 , 1 , 26 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384
+1820 */{ 0 , 2 , 14 , 21968 },/* 29 30 29 30 29 30 29 30 30 30 29 30 0 355
+1821 */{ 0 , 2 , 3 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1822 */{ 3 , 1 , 23 , 41688 },/* 30 29 30 29 29 29 30 29 30 30 29 30 30 384
+1823 */{ 0 , 2 , 11 , 41648 },/* 30 29 30 29 29 29 30 29 30 29 30 30 0 354
+1824 */{ 7 , 1 , 31 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1825 */{ 0 , 2 , 18 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1826 */{ 0 , 2 , 7 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+1827 */{ 5 , 1 , 27 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+1828 */{ 0 , 2 , 15 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+1829 */{ 0 , 2 , 4 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355
+1830 */{ 4 , 1 , 25 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384
+1831 */{ 0 , 2 , 13 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354
+1832 */{ 9 , 2 , 2 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+1833 */{ 0 , 2 , 20 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1834 */{ 0 , 2 , 9 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354
+1835 */{ 6 , 1 , 29 , 27816 },/* 29 30 30 29 30 30 29 29 30 29 30 29 30 384
+1836 */{ 0 , 2 , 17 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1837 */{ 0 , 2 , 5 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1838 */{ 4 , 1 , 26 , 21352 },/* 29 30 29 30 29 29 30 30 29 30 30 29 30 384
+1839 */{ 0 , 2 , 14 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1840 */{ 0 , 2 , 3 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1841 */{ 3 , 1 , 23 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 29 383
+1842 */{ 0 , 2 , 10 , 53920 },/* 30 30 29 30 29 29 30 29 30 29 30 29 0 354
+1843 */{ 7 , 1 , 30 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 29 384
+1844 */{ 0 , 2 , 18 , 54608 },/* 30 30 29 30 29 30 29 30 29 30 29 30 0 355
+1845 */{ 0 , 2 , 7 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+1846 */{ 5 , 1 , 27 , 43728 },/* 30 29 30 29 30 29 30 29 30 30 29 30 29 384
+1847 */{ 0 , 2 , 15 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355
+1848 */{ 0 , 2 , 5 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1849 */{ 4 , 1 , 24 , 42328 },/* 30 29 30 29 29 30 29 30 29 30 29 30 30 384
+1850 */{ 0 , 2 , 12 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1851 */{ 8 , 2 , 1 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384
+1852 */{ 0 , 2 , 20 , 45712 },/* 30 29 30 30 29 29 30 29 30 29 29 30 0 354
+1853 */{ 0 , 2 , 8 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1854 */{ 7 , 1 , 29 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1855 */{ 0 , 2 , 17 , 11680 },/* 29 29 30 29 30 30 29 30 30 29 30 29 0 354
+1856 */{ 0 , 2 , 6 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355
+1857 */{ 5 , 1 , 26 , 19128 },/* 29 30 29 29 30 29 30 29 30 29 30 30 30 384
+1858 */{ 0 , 2 , 14 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1859 */{ 0 , 2 , 3 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1860 */{ 3 , 1 , 23 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1861 */{ 0 , 2 , 10 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354
+1862 */{ 8 , 1 , 30 , 44360 },/* 30 29 30 29 30 30 29 30 29 30 29 29 30 384
+1863 */{ 0 , 2 , 18 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 0 355
+1864 */{ 0 , 2 , 8 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1865 */{ 5 , 1 , 27 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384
+1866 */{ 0 , 2 , 15 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 0 355
+1867 */{ 0 , 2 , 5 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1868 */{ 4 , 1 , 25 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383
+1869 */{ 0 , 2 , 11 , 58528 },/* 30 30 30 29 29 30 29 29 30 29 30 29 0 354
+1870 */{ 10 , 1 , 31 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 29 384
+1871 */{ 0 , 2 , 19 , 55952 },/* 30 30 29 30 30 29 30 29 30 29 29 30 0 355
+1872 */{ 0 , 2 , 9 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355
+1873 */{ 6 , 1 , 29 , 11112 },/* 29 29 30 29 30 29 30 30 29 30 30 29 30 384
+1874 */{ 0 , 2 , 17 , 10976 },/* 29 29 30 29 30 29 30 29 30 30 30 29 0 354
+1875 */{ 0 , 2 , 6 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1876 */{ 5 , 1 , 26 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384
+1877 */{ 0 , 2 , 13 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354
+1878 */{ 0 , 2 , 2 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1879 */{ 3 , 1 , 22 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384
+1880 */{ 0 , 2 , 10 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 0 355
+1881 */{ 7 , 1 , 30 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 29 384
+1882 */{ 0 , 2 , 18 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355
+1883 */{ 0 , 2 , 8 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1884 */{ 5 , 1 , 28 , 37592 },/* 30 29 29 30 29 29 30 29 30 30 29 30 30 384
+1885 */{ 0 , 2 , 15 , 37552 },/* 30 29 29 30 29 29 30 29 30 29 30 30 0 354
+1886 */{ 0 , 2 , 4 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1887 */{ 4 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1888 */{ 0 , 2 , 12 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+1889 */{ 0 , 1 , 31 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1890 */{ 2 , 1 , 21 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1891 */{ 0 , 2 , 9 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 0 355
+1892 */{ 6 , 1 , 30 , 9656 },/* 29 29 30 29 29 30 29 30 30 29 30 30 30 384
+1893 */{ 0 , 2 , 17 , 9584 },/* 29 29 30 29 29 30 29 30 29 30 30 30 0 354
+1894 */{ 0 , 2 , 6 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1895 */{ 5 , 1 , 26 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383
+1896 */{ 0 , 2 , 13 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355
+1897 */{ 0 , 2 , 2 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1898 */{ 3 , 1 , 22 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1899 */{ 0 , 2 , 10 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1900 */{ 8 , 1 , 31 , 19304 },/* 29 30 29 29 30 29 30 30 29 30 30 29 30 384
+1901 */{ 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1902 */{ 0 , 2 , 8 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1903 */{ 5 , 1 , 29 , 21096 },/* 29 30 29 30 29 29 30 29 29 30 30 29 30 383
+1904 */{ 0 , 2 , 16 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+1905 */{ 0 , 2 , 4 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355
+1906 */{ 4 , 1 , 25 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384
+1907 */{ 0 , 2 , 13 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1908 */{ 0 , 2 , 2 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355
+1909 */{ 2 , 1 , 22 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1910 */{ 0 , 2 , 10 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1911 */{ 6 , 1 , 30 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+1912 */{ 0 , 2 , 18 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1913 */{ 0 , 2 , 6 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1914 */{ 5 , 1 , 26 , 55624 },/* 30 30 29 30 30 29 29 30 29 30 29 29 30 384
+1915 */{ 0 , 2 , 14 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1916 */{ 0 , 2 , 4 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1917 */{ 2 , 1 , 23 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384
+1918 */{ 0 , 2 , 11 , 38352 },/* 30 29 29 30 29 30 29 30 30 30 29 30 0 355
+1919 */{ 7 , 2 , 1 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384
+1920 */{ 0 , 2 , 20 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1921 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1922 */{ 5 , 1 , 28 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1923 */{ 0 , 2 , 16 , 27280 },/* 29 30 30 29 30 29 30 29 30 29 29 30 0 354
+1924 */{ 0 , 2 , 5 , 44352 },/* 30 29 30 29 30 30 29 30 29 30 29 29 0 354
+1925 */{ 4 , 1 , 24 , 46504 },/* 30 29 30 30 29 30 29 30 30 29 30 29 30 385
+1926 */{ 0 , 2 , 13 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1927 */{ 0 , 2 , 2 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1928 */{ 2 , 1 , 23 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384
+1929 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1930 */{ 6 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383
+1931 */{ 0 , 2 , 17 , 58528 },/* 30 30 30 29 29 30 29 29 30 29 30 29 0 354
+1932 */{ 0 , 2 , 6 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355
+1933 */{ 5 , 1 , 26 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384
+1934 */{ 0 , 2 , 14 , 23376 },/* 29 30 29 30 30 29 30 30 29 30 29 30 0 355
+1935 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1936 */{ 3 , 1 , 24 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 29 384
+1937 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1938 */{ 7 , 1 , 31 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384
+1939 */{ 0 , 2 , 19 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354
+1940 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1941 */{ 6 , 1 , 27 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384
+1942 */{ 0 , 2 , 15 , 46736 },/* 30 29 30 30 29 30 30 29 30 29 29 30 0 355
+1943 */{ 0 , 2 , 5 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+1944 */{ 4 , 1 , 26 , 10968 },/* 29 29 30 29 30 29 30 29 30 30 29 30 30 384
+1945 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1946 */{ 0 , 2 , 2 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354
+1947 */{ 2 , 1 , 22 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384
+1948 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1949 */{ 7 , 1 , 29 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+1950 */{ 0 , 2 , 17 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+1951 */{ 0 , 2 , 6 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1952 */{ 5 , 1 , 27 , 22184 },/* 29 30 29 30 29 30 30 29 30 29 30 29 30 384
+1953 */{ 0 , 2 , 14 , 19888 },/* 29 30 29 29 30 30 29 30 30 29 30 30 0 355
+1954 */{ 0 , 2 , 4 , 9648 },/* 29 29 30 29 29 30 29 30 30 29 30 30 0 354
+1955 */{ 3 , 1 , 24 , 37560 },/* 30 29 29 30 29 29 30 29 30 29 30 30 30 384
+1956 */{ 0 , 2 , 12 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1957 */{ 8 , 1 , 31 , 43352 },/* 30 29 30 29 30 29 29 30 29 30 29 30 30 384
+1958 */{ 0 , 2 , 19 , 26960 },/* 29 30 30 29 30 29 29 30 29 30 29 30 0 354
+1959 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1960 */{ 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1962 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1965 */{ 0 , 2 , 2 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354
+1966 */{ 3 , 1 , 22 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383
+1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355
+1968 */{ 7 , 1 , 30 , 27304 },/* 29 30 30 29 30 29 30 29 30 29 30 29 30 384
+1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1970 */{ 0 , 2 , 6 , 39632 },/* 30 29 29 30 30 29 30 29 30 30 29 30 0 355
+1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1972 */{ 0 , 2 , 15 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+1973 */{ 0 , 2 , 3 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354
+1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1976 */{ 8 , 1 , 31 , 54600 },/* 30 30 29 30 29 30 29 30 29 30 29 29 30 384
+1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354
+1978 */{ 0 , 2 , 7 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355
+1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384
+1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1982 */{ 4 , 1 , 25 , 42200 },/* 30 29 30 29 29 30 29 29 30 30 29 30 30 384
+1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+1987 */{ 6 , 1 , 29 , 46504 },/* 30 29 30 30 29 30 29 30 30 29 30 29 30 385
+1988 */{ 0 , 2 , 18 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1989 */{ 0 , 2 , 6 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1990 */{ 5 , 1 , 27 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384
+1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355
+1995 */{ 8 , 1 , 31 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384
+1996 */{ 0 , 2 , 19 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355
+1997 */{ 0 , 2 , 8 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1998 */{ 5 , 1 , 28 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384
+1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+2001 */{ 4 , 1 , 24 , 58536 },/* 30 30 30 29 29 30 29 29 30 29 30 29 30 384
+2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+2005 */{ 0 , 2 , 9 , 22208 },/* 29 30 29 30 29 30 30 29 30 30 29 29 0 354
+2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385
+2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354
+2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384
+2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+2012 */{ 3 , 1 , 23 , 47696 },/* 30 29 30 30 30 29 30 29 29 30 29 30 29 384
+2013 */{ 0 , 2 , 10 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384
+2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354
+2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+2017 */{ 5 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+2019 */{ 0 , 2 , 5 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+2020 */{ 4 , 1 , 25 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384
+2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+2023 */{ 2 , 1 , 22 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384
+2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+2026 */{ 0 , 2 , 17 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+2027 */{ 0 , 2 , 7 , 21104 },/* 29 30 29 30 29 29 30 29 29 30 30 30 0 354
+2028 */{ 5 , 1 , 27 , 26928 },/* 29 30 30 29 30 29 29 30 29 29 30 30 29 383
+2029 */{ 0 , 2 , 13 , 55600 },/* 30 30 29 30 30 29 29 30 29 29 30 30 0 355
+2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+2031 */{ 3 , 1 , 23 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 29 384
+2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+2034 */{ 0 , 2 , 19 , 19168 },/* 29 30 29 29 30 29 30 29 30 30 30 29 0 354
+2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+2036 */{ 6 , 1 , 28 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354
+2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384
+2040 */{ 0 , 2 , 12 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355
+2041 */{ 0 , 2 , 1 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384
+2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+2046 */{ 0 , 2 , 6 , 45648 },/* 30 29 30 30 29 29 30 29 29 30 29 30 0 354
+2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+2048 */{ 0 , 2 , 14 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355
+2050 */{ 3 , 1 , 23 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 29 384
+ */};
+
+
+ internal override int MinCalendarYear
+ {
+ get
+ {
+ return (MIN_LUNISOLAR_YEAR);
+ }
+ }
+
+ internal override int MaxCalendarYear
+ {
+ get
+ {
+ return (MAX_LUNISOLAR_YEAR);
+ }
+ }
+
+ internal override DateTime MinDate
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+ internal override DateTime MaxDate
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+
+ internal override EraInfo[] CalEraInfo
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ internal override int GetYearInfo(int lunarYear, int index)
+ {
+ if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR))
+ {
+ throw new ArgumentOutOfRangeException(
+ "year",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ MIN_LUNISOLAR_YEAR,
+ MAX_LUNISOLAR_YEAR));
+ }
+ Contract.EndContractBlock();
+ return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index];
+ }
+
+ internal override int GetYear(int year, DateTime time)
+ {
+ return year;
+ }
+
+ internal override int GetGregorianYear(int year, int era)
+ {
+ if (era != CurrentEra && era != GregorianEra)
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+
+ if (year < MIN_LUNISOLAR_YEAR || year > MAX_LUNISOLAR_YEAR)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range, MIN_LUNISOLAR_YEAR, MAX_LUNISOLAR_YEAR));
+ }
+ Contract.EndContractBlock();
+
+ return year;
+ }
+
+ public KoreanLunisolarCalendar()
+ {
+ }
+
+ public override int GetEra(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+ return (GregorianEra);
+ }
+
+ internal override CalendarId BaseCalendarID
+ {
+ get
+ {
+ return (CalendarId.KOREA);
+ }
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return (CalendarId.KOREANLUNISOLAR);
+ }
+ }
+
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (new int[] { GregorianEra });
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/LocaleData.Unix.cs b/src/mscorlib/shared/System/Globalization/LocaleData.Unix.cs
new file mode 100644
index 0000000000..d4c58d8a3d
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/LocaleData.Unix.cs
@@ -0,0 +1,4572 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Diagnostics.Contracts;
+
+// This file contains the handling of Windows OS specific culture features.
+
+namespace System.Globalization
+{
+ internal enum LocaleDataParts
+ {
+ Lcid = 0,
+ AnsiCodePage = 1,
+ OemCodePage = 2,
+ MacCodePage = 3,
+ EbcdicCodePage = 4,
+ GeoId = 5,
+ DigitSubstitution = 6,
+ SpecificLocaleIndex = 7,
+ ConsoleLocaleIndex = 8
+ }
+
+ internal partial class LocaleData
+ {
+ // this is done rather than using a large readonly array of strings to avoid
+ // generating a large amount of code in the static constructor.
+ // Using indices from s_localeNamesIndices, we binary search this string when mapping
+ // an culture name to Lcid. Note that these names are all lowercase and are
+ // sorted alphabetically (ordinal).
+ private const string c_localeNames =
+ // culture name Lcid
+ "aa" + // 01000 - 0
+ "aa-dj" + // 01000 - 2
+ "aa-er" + // 01000 - 7
+ "aa-et" + // 01000 - 12
+ "af" + // 00036 - 17
+ "af-na" + // 01000 - 19
+ "af-za" + // 00436 - 24
+ "agq" + // 01000 - 29
+ "agq-cm" + // 01000 - 32
+ "ak" + // 01000 - 38
+ "ak-gh" + // 01000 - 40
+ "am" + // 0005e - 45
+ "am-et" + // 0045e - 47
+ "ar" + // 00001 - 52
+ "ar-001" + // 01000 - 54
+ "ar-ae" + // 03801 - 60
+ "ar-bh" + // 03c01 - 65
+ "ar-dj" + // 01000 - 70
+ "ar-dz" + // 01401 - 75
+ "ar-eg" + // 00c01 - 80
+ "ar-er" + // 01000 - 85
+ "ar-il" + // 01000 - 90
+ "ar-iq" + // 00801 - 95
+ "ar-jo" + // 02c01 - 100
+ "ar-km" + // 01000 - 105
+ "ar-kw" + // 03401 - 110
+ "ar-lb" + // 03001 - 115
+ "ar-ly" + // 01001 - 120
+ "ar-ma" + // 01801 - 125
+ "ar-mr" + // 01000 - 130
+ "ar-om" + // 02001 - 135
+ "ar-ps" + // 01000 - 140
+ "ar-qa" + // 04001 - 145
+ "ar-sa" + // 00401 - 150
+ "ar-sd" + // 01000 - 155
+ "ar-so" + // 01000 - 160
+ "ar-ss" + // 01000 - 165
+ "ar-sy" + // 02801 - 170
+ "ar-td" + // 01000 - 175
+ "ar-tn" + // 01c01 - 180
+ "ar-ye" + // 02401 - 185
+ "arn" + // 0007a - 190
+ "arn-cl" + // 0047a - 193
+ "as" + // 0004d - 199
+ "as-in" + // 0044d - 201
+ "asa" + // 01000 - 206
+ "asa-tz" + // 01000 - 209
+ "ast" + // 01000 - 215
+ "ast-es" + // 01000 - 218
+ "az" + // 0002c - 224
+ "az-cyrl" + // 0742c - 226
+ "az-cyrl-az" + // 0082c - 233
+ "az-latn" + // 0782c - 243
+ "az-latn-az" + // 0042c - 250
+ "ba" + // 0006d - 260
+ "ba-ru" + // 0046d - 262
+ "bas" + // 01000 - 267
+ "bas-cm" + // 01000 - 270
+ "be" + // 00023 - 276
+ "be-by" + // 00423 - 278
+ "bem" + // 01000 - 283
+ "bem-zm" + // 01000 - 286
+ "bez" + // 01000 - 292
+ "bez-tz" + // 01000 - 295
+ "bg" + // 00002 - 301
+ "bg-bg" + // 00402 - 303
+ "bin" + // 00066 - 308
+ "bin-ng" + // 00466 - 311
+ "bm" + // 01000 - 317
+ "bm-latn" + // 01000 - 319
+ "bm-latn-ml" + // 01000 - 326
+ "bn" + // 00045 - 336
+ "bn-bd" + // 00845 - 338
+ "bn-in" + // 00445 - 343
+ "bo" + // 00051 - 348
+ "bo-cn" + // 00451 - 350
+ "bo-in" + // 01000 - 355
+ "br" + // 0007e - 360
+ "br-fr" + // 0047e - 362
+ "brx" + // 01000 - 367
+ "brx-in" + // 01000 - 370
+ "bs" + // 0781a - 376
+ "bs-cyrl" + // 0641a - 378
+ "bs-cyrl-ba" + // 0201a - 385
+ "bs-latn" + // 0681a - 395
+ "bs-latn-ba" + // 0141a - 402
+ "byn" + // 01000 - 412
+ "byn-er" + // 01000 - 415
+ "ca" + // 00003 - 421
+ "ca-ad" + // 01000 - 423
+ "ca-es" + // 00403 - 428
+ "ca-es-valencia" + // 00803 - 433
+ "ca-fr" + // 01000 - 447
+ "ca-it" + // 01000 - 452
+ "ce" + // 01000 - 457
+ "ce-ru" + // 01000 - 459
+ "cgg" + // 01000 - 464
+ "cgg-ug" + // 01000 - 467
+ "chr" + // 0005c - 473
+ "chr-cher" + // 07c5c - 476
+ "chr-cher-us" + // 0045c - 484
+ "co" + // 00083 - 495
+ "co-fr" + // 00483 - 497
+ "cs" + // 00005 - 502
+ "cs-cz" + // 00405 - 504
+ "cu" + // 01000 - 509
+ "cu-ru" + // 01000 - 511
+ "cy" + // 00052 - 516
+ "cy-gb" + // 00452 - 518
+ "da" + // 00006 - 523
+ "da-dk" + // 00406 - 525
+ "da-gl" + // 01000 - 530
+ "dav" + // 01000 - 535
+ "dav-ke" + // 01000 - 538
+ "de" + // 00007 - 544
+ "de-at" + // 00c07 - 546
+ "de-be" + // 01000 - 551
+ "de-ch" + // 00807 - 556
+ "de-de" + // 00407 - 561
+ "de-de_phoneb" + // 10407 - 566
+ "de-it" + // 01000 - 578
+ "de-li" + // 01407 - 583
+ "de-lu" + // 01007 - 588
+ "dje" + // 01000 - 593
+ "dje-ne" + // 01000 - 596
+ "dsb" + // 07c2e - 602
+ "dsb-de" + // 0082e - 605
+ "dua" + // 01000 - 611
+ "dua-cm" + // 01000 - 614
+ "dv" + // 00065 - 620
+ "dv-mv" + // 00465 - 622
+ "dyo" + // 01000 - 627
+ "dyo-sn" + // 01000 - 630
+ "dz" + // 01000 - 636
+ "dz-bt" + // 00c51 - 638
+ "ebu" + // 01000 - 643
+ "ebu-ke" + // 01000 - 646
+ "ee" + // 01000 - 652
+ "ee-gh" + // 01000 - 654
+ "ee-tg" + // 01000 - 659
+ "el" + // 00008 - 664
+ "el-cy" + // 01000 - 666
+ "el-gr" + // 00408 - 671
+ "en" + // 00009 - 676
+ "en-001" + // 01000 - 678
+ "en-029" + // 02409 - 684
+ "en-150" + // 01000 - 690
+ "en-ag" + // 01000 - 696
+ "en-ai" + // 01000 - 701
+ "en-as" + // 01000 - 706
+ "en-at" + // 01000 - 711
+ "en-au" + // 00c09 - 716
+ "en-bb" + // 01000 - 721
+ "en-be" + // 01000 - 726
+ "en-bi" + // 01000 - 731
+ "en-bm" + // 01000 - 736
+ "en-bs" + // 01000 - 741
+ "en-bw" + // 01000 - 746
+ "en-bz" + // 02809 - 751
+ "en-ca" + // 01009 - 756
+ "en-cc" + // 01000 - 761
+ "en-ch" + // 01000 - 766
+ "en-ck" + // 01000 - 771
+ "en-cm" + // 01000 - 776
+ "en-cx" + // 01000 - 781
+ "en-cy" + // 01000 - 786
+ "en-de" + // 01000 - 791
+ "en-dk" + // 01000 - 796
+ "en-dm" + // 01000 - 801
+ "en-er" + // 01000 - 806
+ "en-fi" + // 01000 - 811
+ "en-fj" + // 01000 - 816
+ "en-fk" + // 01000 - 821
+ "en-fm" + // 01000 - 826
+ "en-gb" + // 00809 - 831
+ "en-gd" + // 01000 - 836
+ "en-gg" + // 01000 - 841
+ "en-gh" + // 01000 - 846
+ "en-gi" + // 01000 - 851
+ "en-gm" + // 01000 - 856
+ "en-gu" + // 01000 - 861
+ "en-gy" + // 01000 - 866
+ "en-hk" + // 03c09 - 871
+ "en-id" + // 03809 - 876
+ "en-ie" + // 01809 - 881
+ "en-il" + // 01000 - 886
+ "en-im" + // 01000 - 891
+ "en-in" + // 04009 - 896
+ "en-io" + // 01000 - 901
+ "en-je" + // 01000 - 906
+ "en-jm" + // 02009 - 911
+ "en-ke" + // 01000 - 916
+ "en-ki" + // 01000 - 921
+ "en-kn" + // 01000 - 926
+ "en-ky" + // 01000 - 931
+ "en-lc" + // 01000 - 936
+ "en-lr" + // 01000 - 941
+ "en-ls" + // 01000 - 946
+ "en-mg" + // 01000 - 951
+ "en-mh" + // 01000 - 956
+ "en-mo" + // 01000 - 961
+ "en-mp" + // 01000 - 966
+ "en-ms" + // 01000 - 971
+ "en-mt" + // 01000 - 976
+ "en-mu" + // 01000 - 981
+ "en-mw" + // 01000 - 986
+ "en-my" + // 04409 - 991
+ "en-na" + // 01000 - 996
+ "en-nf" + // 01000 - 1001
+ "en-ng" + // 01000 - 1006
+ "en-nl" + // 01000 - 1011
+ "en-nr" + // 01000 - 1016
+ "en-nu" + // 01000 - 1021
+ "en-nz" + // 01409 - 1026
+ "en-pg" + // 01000 - 1031
+ "en-ph" + // 03409 - 1036
+ "en-pk" + // 01000 - 1041
+ "en-pn" + // 01000 - 1046
+ "en-pr" + // 01000 - 1051
+ "en-pw" + // 01000 - 1056
+ "en-rw" + // 01000 - 1061
+ "en-sb" + // 01000 - 1066
+ "en-sc" + // 01000 - 1071
+ "en-sd" + // 01000 - 1076
+ "en-se" + // 01000 - 1081
+ "en-sg" + // 04809 - 1086
+ "en-sh" + // 01000 - 1091
+ "en-si" + // 01000 - 1096
+ "en-sl" + // 01000 - 1101
+ "en-ss" + // 01000 - 1106
+ "en-sx" + // 01000 - 1111
+ "en-sz" + // 01000 - 1116
+ "en-tc" + // 01000 - 1121
+ "en-tk" + // 01000 - 1126
+ "en-to" + // 01000 - 1131
+ "en-tt" + // 02c09 - 1136
+ "en-tv" + // 01000 - 1141
+ "en-tz" + // 01000 - 1146
+ "en-ug" + // 01000 - 1151
+ "en-um" + // 01000 - 1156
+ "en-us" + // 00409 - 1161
+ "en-vc" + // 01000 - 1166
+ "en-vg" + // 01000 - 1171
+ "en-vi" + // 01000 - 1176
+ "en-vu" + // 01000 - 1181
+ "en-ws" + // 01000 - 1186
+ "en-za" + // 01c09 - 1191
+ "en-zm" + // 01000 - 1196
+ "en-zw" + // 03009 - 1201
+ "eo" + // 01000 - 1206
+ "eo-001" + // 01000 - 1208
+ "es" + // 0000a - 1214
+ "es-419" + // 0580a - 1216
+ "es-ar" + // 02c0a - 1222
+ "es-bo" + // 0400a - 1227
+ "es-br" + // 01000 - 1232
+ "es-cl" + // 0340a - 1237
+ "es-co" + // 0240a - 1242
+ "es-cr" + // 0140a - 1247
+ "es-cu" + // 05c0a - 1252
+ "es-do" + // 01c0a - 1257
+ "es-ec" + // 0300a - 1262
+ "es-es" + // 00c0a - 1267
+ "es-es_tradnl" + // 0040a - 1272
+ "es-gq" + // 01000 - 1284
+ "es-gt" + // 0100a - 1289
+ "es-hn" + // 0480a - 1294
+ "es-mx" + // 0080a - 1299
+ "es-ni" + // 04c0a - 1304
+ "es-pa" + // 0180a - 1309
+ "es-pe" + // 0280a - 1314
+ "es-ph" + // 01000 - 1319
+ "es-pr" + // 0500a - 1324
+ "es-py" + // 03c0a - 1329
+ "es-sv" + // 0440a - 1334
+ "es-us" + // 0540a - 1339
+ "es-uy" + // 0380a - 1344
+ "es-ve" + // 0200a - 1349
+ "et" + // 00025 - 1354
+ "et-ee" + // 00425 - 1356
+ "eu" + // 0002d - 1361
+ "eu-es" + // 0042d - 1363
+ "ewo" + // 01000 - 1368
+ "ewo-cm" + // 01000 - 1371
+ "fa" + // 00029 - 1377
+ "fa-ir" + // 00429 - 1379
+ "ff" + // 00067 - 1384
+ "ff-cm" + // 01000 - 1386
+ "ff-gn" + // 01000 - 1391
+ "ff-latn" + // 07c67 - 1396
+ "ff-latn-sn" + // 00867 - 1403
+ "ff-mr" + // 01000 - 1413
+ "ff-ng" + // 00467 - 1418
+ "fi" + // 0000b - 1423
+ "fi-fi" + // 0040b - 1425
+ "fil" + // 00064 - 1430
+ "fil-ph" + // 00464 - 1433
+ "fo" + // 00038 - 1439
+ "fo-dk" + // 01000 - 1441
+ "fo-fo" + // 00438 - 1446
+ "fr" + // 0000c - 1451
+ "fr-029" + // 01c0c - 1453
+ "fr-be" + // 0080c - 1459
+ "fr-bf" + // 01000 - 1464
+ "fr-bi" + // 01000 - 1469
+ "fr-bj" + // 01000 - 1474
+ "fr-bl" + // 01000 - 1479
+ "fr-ca" + // 00c0c - 1484
+ "fr-cd" + // 0240c - 1489
+ "fr-cf" + // 01000 - 1494
+ "fr-cg" + // 01000 - 1499
+ "fr-ch" + // 0100c - 1504
+ "fr-ci" + // 0300c - 1509
+ "fr-cm" + // 02c0c - 1514
+ "fr-dj" + // 01000 - 1519
+ "fr-dz" + // 01000 - 1524
+ "fr-fr" + // 0040c - 1529
+ "fr-ga" + // 01000 - 1534
+ "fr-gf" + // 01000 - 1539
+ "fr-gn" + // 01000 - 1544
+ "fr-gp" + // 01000 - 1549
+ "fr-gq" + // 01000 - 1554
+ "fr-ht" + // 03c0c - 1559
+ "fr-km" + // 01000 - 1564
+ "fr-lu" + // 0140c - 1569
+ "fr-ma" + // 0380c - 1574
+ "fr-mc" + // 0180c - 1579
+ "fr-mf" + // 01000 - 1584
+ "fr-mg" + // 01000 - 1589
+ "fr-ml" + // 0340c - 1594
+ "fr-mq" + // 01000 - 1599
+ "fr-mr" + // 01000 - 1604
+ "fr-mu" + // 01000 - 1609
+ "fr-nc" + // 01000 - 1614
+ "fr-ne" + // 01000 - 1619
+ "fr-pf" + // 01000 - 1624
+ "fr-pm" + // 01000 - 1629
+ "fr-re" + // 0200c - 1634
+ "fr-rw" + // 01000 - 1639
+ "fr-sc" + // 01000 - 1644
+ "fr-sn" + // 0280c - 1649
+ "fr-sy" + // 01000 - 1654
+ "fr-td" + // 01000 - 1659
+ "fr-tg" + // 01000 - 1664
+ "fr-tn" + // 01000 - 1669
+ "fr-vu" + // 01000 - 1674
+ "fr-wf" + // 01000 - 1679
+ "fr-yt" + // 01000 - 1684
+ "fur" + // 01000 - 1689
+ "fur-it" + // 01000 - 1692
+ "fy" + // 00062 - 1698
+ "fy-nl" + // 00462 - 1700
+ "ga" + // 0003c - 1705
+ "ga-ie" + // 0083c - 1707
+ "gd" + // 00091 - 1712
+ "gd-gb" + // 00491 - 1714
+ "gl" + // 00056 - 1719
+ "gl-es" + // 00456 - 1721
+ "gn" + // 00074 - 1726
+ "gn-py" + // 00474 - 1728
+ "gsw" + // 00084 - 1733
+ "gsw-ch" + // 01000 - 1736
+ "gsw-fr" + // 00484 - 1742
+ "gsw-li" + // 01000 - 1748
+ "gu" + // 00047 - 1754
+ "gu-in" + // 00447 - 1756
+ "guz" + // 01000 - 1761
+ "guz-ke" + // 01000 - 1764
+ "gv" + // 01000 - 1770
+ "gv-im" + // 01000 - 1772
+ "ha" + // 00068 - 1777
+ "ha-latn" + // 07c68 - 1779
+ "ha-latn-gh" + // 01000 - 1786
+ "ha-latn-ne" + // 01000 - 1796
+ "ha-latn-ng" + // 00468 - 1806
+ "haw" + // 00075 - 1816
+ "haw-us" + // 00475 - 1819
+ "he" + // 0000d - 1825
+ "he-il" + // 0040d - 1827
+ "hi" + // 00039 - 1832
+ "hi-in" + // 00439 - 1834
+ "hr" + // 0001a - 1839
+ "hr-ba" + // 0101a - 1841
+ "hr-hr" + // 0041a - 1846
+ "hsb" + // 0002e - 1851
+ "hsb-de" + // 0042e - 1854
+ "hu" + // 0000e - 1860
+ "hu-hu" + // 0040e - 1862
+ "hu-hu_technl" + // 1040e - 1867
+ "hy" + // 0002b - 1879
+ "hy-am" + // 0042b - 1881
+ "ia" + // 01000 - 1886
+ "ia-001" + // 01000 - 1888
+ "ia-fr" + // 01000 - 1894
+ "ibb" + // 00069 - 1899
+ "ibb-ng" + // 00469 - 1902
+ "id" + // 00021 - 1908
+ "id-id" + // 00421 - 1910
+ "ig" + // 00070 - 1915
+ "ig-ng" + // 00470 - 1917
+ "ii" + // 00078 - 1922
+ "ii-cn" + // 00478 - 1924
+ "is" + // 0000f - 1929
+ "is-is" + // 0040f - 1931
+ "it" + // 00010 - 1936
+ "it-ch" + // 00810 - 1938
+ "it-it" + // 00410 - 1943
+ "it-sm" + // 01000 - 1948
+ "iu" + // 0005d - 1953
+ "iu-cans" + // 0785d - 1955
+ "iu-cans-ca" + // 0045d - 1962
+ "iu-latn" + // 07c5d - 1972
+ "iu-latn-ca" + // 0085d - 1979
+ "ja" + // 00011 - 1989
+ "ja-jp" + // 00411 - 1991
+ "ja-jp_radstr" + // 40411 - 1996
+ "jgo" + // 01000 - 2008
+ "jgo-cm" + // 01000 - 2011
+ "jmc" + // 01000 - 2017
+ "jmc-tz" + // 01000 - 2020
+ "jv" + // 01000 - 2026
+ "jv-java" + // 01000 - 2028
+ "jv-java-id" + // 01000 - 2035
+ "jv-latn" + // 01000 - 2045
+ "jv-latn-id" + // 01000 - 2052
+ "ka" + // 00037 - 2062
+ "ka-ge" + // 00437 - 2064
+ "ka-ge_modern" + // 10437 - 2069
+ "kab" + // 01000 - 2081
+ "kab-dz" + // 01000 - 2084
+ "kam" + // 01000 - 2090
+ "kam-ke" + // 01000 - 2093
+ "kde" + // 01000 - 2099
+ "kde-tz" + // 01000 - 2102
+ "kea" + // 01000 - 2108
+ "kea-cv" + // 01000 - 2111
+ "khq" + // 01000 - 2117
+ "khq-ml" + // 01000 - 2120
+ "ki" + // 01000 - 2126
+ "ki-ke" + // 01000 - 2128
+ "kk" + // 0003f - 2133
+ "kk-kz" + // 0043f - 2135
+ "kkj" + // 01000 - 2140
+ "kkj-cm" + // 01000 - 2143
+ "kl" + // 0006f - 2149
+ "kl-gl" + // 0046f - 2151
+ "kln" + // 01000 - 2156
+ "kln-ke" + // 01000 - 2159
+ "km" + // 00053 - 2165
+ "km-kh" + // 00453 - 2167
+ "kn" + // 0004b - 2172
+ "kn-in" + // 0044b - 2174
+ "ko" + // 00012 - 2179
+ "ko-kp" + // 01000 - 2181
+ "ko-kr" + // 00412 - 2186
+ "kok" + // 00057 - 2191
+ "kok-in" + // 00457 - 2194
+ "kr" + // 00071 - 2200
+ "kr-ng" + // 00471 - 2202
+ "ks" + // 00060 - 2207
+ "ks-arab" + // 00460 - 2209
+ "ks-arab-in" + // 01000 - 2216
+ "ks-deva" + // 01000 - 2226
+ "ks-deva-in" + // 00860 - 2233
+ "ksb" + // 01000 - 2243
+ "ksb-tz" + // 01000 - 2246
+ "ksf" + // 01000 - 2252
+ "ksf-cm" + // 01000 - 2255
+ "ksh" + // 01000 - 2261
+ "ksh-de" + // 01000 - 2264
+ "ku" + // 00092 - 2270
+ "ku-arab" + // 07c92 - 2272
+ "ku-arab-iq" + // 00492 - 2279
+ "ku-arab-ir" + // 01000 - 2289
+ "kw" + // 01000 - 2299
+ "kw-gb" + // 01000 - 2301
+ "ky" + // 00040 - 2306
+ "ky-kg" + // 00440 - 2308
+ "la" + // 00076 - 2313
+ "la-001" + // 00476 - 2315
+ "lag" + // 01000 - 2321
+ "lag-tz" + // 01000 - 2324
+ "lb" + // 0006e - 2330
+ "lb-lu" + // 0046e - 2332
+ "lg" + // 01000 - 2337
+ "lg-ug" + // 01000 - 2339
+ "lkt" + // 01000 - 2344
+ "lkt-us" + // 01000 - 2347
+ "ln" + // 01000 - 2353
+ "ln-ao" + // 01000 - 2355
+ "ln-cd" + // 01000 - 2360
+ "ln-cf" + // 01000 - 2365
+ "ln-cg" + // 01000 - 2370
+ "lo" + // 00054 - 2375
+ "lo-la" + // 00454 - 2377
+ "lrc" + // 01000 - 2382
+ "lrc-iq" + // 01000 - 2385
+ "lrc-ir" + // 01000 - 2391
+ "lt" + // 00027 - 2397
+ "lt-lt" + // 00427 - 2399
+ "lu" + // 01000 - 2404
+ "lu-cd" + // 01000 - 2406
+ "luo" + // 01000 - 2411
+ "luo-ke" + // 01000 - 2414
+ "luy" + // 01000 - 2420
+ "luy-ke" + // 01000 - 2423
+ "lv" + // 00026 - 2429
+ "lv-lv" + // 00426 - 2431
+ "mas" + // 01000 - 2436
+ "mas-ke" + // 01000 - 2439
+ "mas-tz" + // 01000 - 2445
+ "mer" + // 01000 - 2451
+ "mer-ke" + // 01000 - 2454
+ "mfe" + // 01000 - 2460
+ "mfe-mu" + // 01000 - 2463
+ "mg" + // 01000 - 2469
+ "mg-mg" + // 01000 - 2471
+ "mgh" + // 01000 - 2476
+ "mgh-mz" + // 01000 - 2479
+ "mgo" + // 01000 - 2485
+ "mgo-cm" + // 01000 - 2488
+ "mi" + // 00081 - 2494
+ "mi-nz" + // 00481 - 2496
+ "mk" + // 0002f - 2501
+ "mk-mk" + // 0042f - 2503
+ "ml" + // 0004c - 2508
+ "ml-in" + // 0044c - 2510
+ "mn" + // 00050 - 2515
+ "mn-cyrl" + // 07850 - 2517
+ "mn-mn" + // 00450 - 2524
+ "mn-mong" + // 07c50 - 2529
+ "mn-mong-cn" + // 00850 - 2536
+ "mn-mong-mn" + // 00c50 - 2546
+ "mni" + // 00058 - 2556
+ "mni-in" + // 00458 - 2559
+ "moh" + // 0007c - 2565
+ "moh-ca" + // 0047c - 2568
+ "mr" + // 0004e - 2574
+ "mr-in" + // 0044e - 2576
+ "ms" + // 0003e - 2581
+ "ms-bn" + // 0083e - 2583
+ "ms-my" + // 0043e - 2588
+ "ms-sg" + // 01000 - 2593
+ "mt" + // 0003a - 2598
+ "mt-mt" + // 0043a - 2600
+ "mua" + // 01000 - 2605
+ "mua-cm" + // 01000 - 2608
+ "my" + // 00055 - 2614
+ "my-mm" + // 00455 - 2616
+ "mzn" + // 01000 - 2621
+ "mzn-ir" + // 01000 - 2624
+ "naq" + // 01000 - 2630
+ "naq-na" + // 01000 - 2633
+ "nb" + // 07c14 - 2639
+ "nb-no" + // 00414 - 2641
+ "nb-sj" + // 01000 - 2646
+ "nd" + // 01000 - 2651
+ "nd-zw" + // 01000 - 2653
+ "nds" + // 01000 - 2658
+ "nds-de" + // 01000 - 2661
+ "nds-nl" + // 01000 - 2667
+ "ne" + // 00061 - 2673
+ "ne-in" + // 00861 - 2675
+ "ne-np" + // 00461 - 2680
+ "nl" + // 00013 - 2685
+ "nl-aw" + // 01000 - 2687
+ "nl-be" + // 00813 - 2692
+ "nl-bq" + // 01000 - 2697
+ "nl-cw" + // 01000 - 2702
+ "nl-nl" + // 00413 - 2707
+ "nl-sr" + // 01000 - 2712
+ "nl-sx" + // 01000 - 2717
+ "nmg" + // 01000 - 2722
+ "nmg-cm" + // 01000 - 2725
+ "nn" + // 07814 - 2731
+ "nn-no" + // 00814 - 2733
+ "nnh" + // 01000 - 2738
+ "nnh-cm" + // 01000 - 2741
+ "no" + // 00014 - 2747
+ "nqo" + // 01000 - 2749
+ "nqo-gn" + // 01000 - 2752
+ "nr" + // 01000 - 2758
+ "nr-za" + // 01000 - 2760
+ "nso" + // 0006c - 2765
+ "nso-za" + // 0046c - 2768
+ "nus" + // 01000 - 2774
+ "nus-ss" + // 01000 - 2777
+ "nyn" + // 01000 - 2783
+ "nyn-ug" + // 01000 - 2786
+ "oc" + // 00082 - 2792
+ "oc-fr" + // 00482 - 2794
+ "om" + // 00072 - 2799
+ "om-et" + // 00472 - 2801
+ "om-ke" + // 01000 - 2806
+ "or" + // 00048 - 2811
+ "or-in" + // 00448 - 2813
+ "os" + // 01000 - 2818
+ "os-ge" + // 01000 - 2820
+ "os-ru" + // 01000 - 2825
+ "pa" + // 00046 - 2830
+ "pa-arab" + // 07c46 - 2832
+ "pa-arab-pk" + // 00846 - 2839
+ "pa-in" + // 00446 - 2849
+ "pap" + // 00079 - 2854
+ "pap-029" + // 00479 - 2857
+ "pl" + // 00015 - 2864
+ "pl-pl" + // 00415 - 2866
+ "prg" + // 01000 - 2871
+ "prg-001" + // 01000 - 2874
+ "prs" + // 0008c - 2881
+ "prs-af" + // 0048c - 2884
+ "ps" + // 00063 - 2890
+ "ps-af" + // 00463 - 2892
+ "pt" + // 00016 - 2897
+ "pt-ao" + // 01000 - 2899
+ "pt-br" + // 00416 - 2904
+ "pt-ch" + // 01000 - 2909
+ "pt-cv" + // 01000 - 2914
+ "pt-gq" + // 01000 - 2919
+ "pt-gw" + // 01000 - 2924
+ "pt-lu" + // 01000 - 2929
+ "pt-mo" + // 01000 - 2934
+ "pt-mz" + // 01000 - 2939
+ "pt-pt" + // 00816 - 2944
+ "pt-st" + // 01000 - 2949
+ "pt-tl" + // 01000 - 2954
+ "qps-latn-x-sh" + // 00901 - 2959
+ "qps-ploc" + // 00501 - 2972
+ "qps-ploca" + // 005fe - 2980
+ "qps-plocm" + // 009ff - 2989
+ "quc" + // 00086 - 2998
+ "quc-latn" + // 07c86 - 3001
+ "quc-latn-gt" + // 00486 - 3009
+ "quz" + // 0006b - 3020
+ "quz-bo" + // 0046b - 3023
+ "quz-ec" + // 0086b - 3029
+ "quz-pe" + // 00c6b - 3035
+ "rm" + // 00017 - 3041
+ "rm-ch" + // 00417 - 3043
+ "rn" + // 01000 - 3048
+ "rn-bi" + // 01000 - 3050
+ "ro" + // 00018 - 3055
+ "ro-md" + // 00818 - 3057
+ "ro-ro" + // 00418 - 3062
+ "rof" + // 01000 - 3067
+ "rof-tz" + // 01000 - 3070
+ "ru" + // 00019 - 3076
+ "ru-by" + // 01000 - 3078
+ "ru-kg" + // 01000 - 3083
+ "ru-kz" + // 01000 - 3088
+ "ru-md" + // 00819 - 3093
+ "ru-ru" + // 00419 - 3098
+ "ru-ua" + // 01000 - 3103
+ "rw" + // 00087 - 3108
+ "rw-rw" + // 00487 - 3110
+ "rwk" + // 01000 - 3115
+ "rwk-tz" + // 01000 - 3118
+ "sa" + // 0004f - 3124
+ "sa-in" + // 0044f - 3126
+ "sah" + // 00085 - 3131
+ "sah-ru" + // 00485 - 3134
+ "saq" + // 01000 - 3140
+ "saq-ke" + // 01000 - 3143
+ "sbp" + // 01000 - 3149
+ "sbp-tz" + // 01000 - 3152
+ "sd" + // 00059 - 3158
+ "sd-arab" + // 07c59 - 3160
+ "sd-arab-pk" + // 00859 - 3167
+ "sd-deva" + // 01000 - 3177
+ "sd-deva-in" + // 00459 - 3184
+ "se" + // 0003b - 3194
+ "se-fi" + // 00c3b - 3196
+ "se-no" + // 0043b - 3201
+ "se-se" + // 0083b - 3206
+ "seh" + // 01000 - 3211
+ "seh-mz" + // 01000 - 3214
+ "ses" + // 01000 - 3220
+ "ses-ml" + // 01000 - 3223
+ "sg" + // 01000 - 3229
+ "sg-cf" + // 01000 - 3231
+ "shi" + // 01000 - 3236
+ "shi-latn" + // 01000 - 3239
+ "shi-latn-ma" + // 01000 - 3247
+ "shi-tfng" + // 01000 - 3258
+ "shi-tfng-ma" + // 01000 - 3266
+ "si" + // 0005b - 3277
+ "si-lk" + // 0045b - 3279
+ "sk" + // 0001b - 3284
+ "sk-sk" + // 0041b - 3286
+ "sl" + // 00024 - 3291
+ "sl-si" + // 00424 - 3293
+ "sma" + // 0783b - 3298
+ "sma-no" + // 0183b - 3301
+ "sma-se" + // 01c3b - 3307
+ "smj" + // 07c3b - 3313
+ "smj-no" + // 0103b - 3316
+ "smj-se" + // 0143b - 3322
+ "smn" + // 0703b - 3328
+ "smn-fi" + // 0243b - 3331
+ "sms" + // 0743b - 3337
+ "sms-fi" + // 0203b - 3340
+ "sn" + // 01000 - 3346
+ "sn-latn" + // 01000 - 3348
+ "sn-latn-zw" + // 01000 - 3355
+ "so" + // 00077 - 3365
+ "so-dj" + // 01000 - 3367
+ "so-et" + // 01000 - 3372
+ "so-ke" + // 01000 - 3377
+ "so-so" + // 00477 - 3382
+ "sq" + // 0001c - 3387
+ "sq-al" + // 0041c - 3389
+ "sq-mk" + // 01000 - 3394
+ "sq-xk" + // 01000 - 3399
+ "sr" + // 07c1a - 3404
+ "sr-cyrl" + // 06c1a - 3406
+ "sr-cyrl-ba" + // 01c1a - 3413
+ "sr-cyrl-cs" + // 00c1a - 3423
+ "sr-cyrl-me" + // 0301a - 3433
+ "sr-cyrl-rs" + // 0281a - 3443
+ "sr-cyrl-xk" + // 01000 - 3453
+ "sr-latn" + // 0701a - 3463
+ "sr-latn-ba" + // 0181a - 3470
+ "sr-latn-cs" + // 0081a - 3480
+ "sr-latn-me" + // 02c1a - 3490
+ "sr-latn-rs" + // 0241a - 3500
+ "sr-latn-xk" + // 01000 - 3510
+ "ss" + // 01000 - 3520
+ "ss-sz" + // 01000 - 3522
+ "ss-za" + // 01000 - 3527
+ "ssy" + // 01000 - 3532
+ "ssy-er" + // 01000 - 3535
+ "st" + // 00030 - 3541
+ "st-ls" + // 01000 - 3543
+ "st-za" + // 00430 - 3548
+ "sv" + // 0001d - 3553
+ "sv-ax" + // 01000 - 3555
+ "sv-fi" + // 0081d - 3560
+ "sv-se" + // 0041d - 3565
+ "sw" + // 00041 - 3570
+ "sw-cd" + // 01000 - 3572
+ "sw-ke" + // 00441 - 3577
+ "sw-tz" + // 01000 - 3582
+ "sw-ug" + // 01000 - 3587
+ "swc" + // 01000 - 3592
+ "swc-cd" + // 01000 - 3595
+ "syr" + // 0005a - 3601
+ "syr-sy" + // 0045a - 3604
+ "ta" + // 00049 - 3610
+ "ta-in" + // 00449 - 3612
+ "ta-lk" + // 00849 - 3617
+ "ta-my" + // 01000 - 3622
+ "ta-sg" + // 01000 - 3627
+ "te" + // 0004a - 3632
+ "te-in" + // 0044a - 3634
+ "teo" + // 01000 - 3639
+ "teo-ke" + // 01000 - 3642
+ "teo-ug" + // 01000 - 3648
+ "tg" + // 00028 - 3654
+ "tg-cyrl" + // 07c28 - 3656
+ "tg-cyrl-tj" + // 00428 - 3663
+ "th" + // 0001e - 3673
+ "th-th" + // 0041e - 3675
+ "ti" + // 00073 - 3680
+ "ti-er" + // 00873 - 3682
+ "ti-et" + // 00473 - 3687
+ "tig" + // 01000 - 3692
+ "tig-er" + // 01000 - 3695
+ "tk" + // 00042 - 3701
+ "tk-tm" + // 00442 - 3703
+ "tn" + // 00032 - 3708
+ "tn-bw" + // 00832 - 3710
+ "tn-za" + // 00432 - 3715
+ "to" + // 01000 - 3720
+ "to-to" + // 01000 - 3722
+ "tr" + // 0001f - 3727
+ "tr-cy" + // 01000 - 3729
+ "tr-tr" + // 0041f - 3734
+ "ts" + // 00031 - 3739
+ "ts-za" + // 00431 - 3741
+ "tt" + // 00044 - 3746
+ "tt-ru" + // 00444 - 3748
+ "twq" + // 01000 - 3753
+ "twq-ne" + // 01000 - 3756
+ "tzm" + // 0005f - 3762
+ "tzm-arab" + // 01000 - 3765
+ "tzm-arab-ma" + // 0045f - 3773
+ "tzm-latn" + // 07c5f - 3784
+ "tzm-latn-dz" + // 0085f - 3792
+ "tzm-latn-ma" + // 01000 - 3803
+ "tzm-tfng" + // 0785f - 3814
+ "tzm-tfng-ma" + // 0105f - 3822
+ "ug" + // 00080 - 3833
+ "ug-cn" + // 00480 - 3835
+ "uk" + // 00022 - 3840
+ "uk-ua" + // 00422 - 3842
+ "ur" + // 00020 - 3847
+ "ur-in" + // 00820 - 3849
+ "ur-pk" + // 00420 - 3854
+ "uz" + // 00043 - 3859
+ "uz-arab" + // 01000 - 3861
+ "uz-arab-af" + // 01000 - 3868
+ "uz-cyrl" + // 07843 - 3878
+ "uz-cyrl-uz" + // 00843 - 3885
+ "uz-latn" + // 07c43 - 3895
+ "uz-latn-uz" + // 00443 - 3902
+ "vai" + // 01000 - 3912
+ "vai-latn" + // 01000 - 3915
+ "vai-latn-lr" + // 01000 - 3923
+ "vai-vaii" + // 01000 - 3934
+ "vai-vaii-lr" + // 01000 - 3942
+ "ve" + // 00033 - 3953
+ "ve-za" + // 00433 - 3955
+ "vi" + // 0002a - 3960
+ "vi-vn" + // 0042a - 3962
+ "vo" + // 01000 - 3967
+ "vo-001" + // 01000 - 3969
+ "vun" + // 01000 - 3975
+ "vun-tz" + // 01000 - 3978
+ "wae" + // 01000 - 3984
+ "wae-ch" + // 01000 - 3987
+ "wal" + // 01000 - 3993
+ "wal-et" + // 01000 - 3996
+ "wo" + // 00088 - 4002
+ "wo-sn" + // 00488 - 4004
+ "x-iv_mathan" + // 1007f - 4009
+ "xh" + // 00034 - 4020
+ "xh-za" + // 00434 - 4022
+ "xog" + // 01000 - 4027
+ "xog-ug" + // 01000 - 4030
+ "yav" + // 01000 - 4036
+ "yav-cm" + // 01000 - 4039
+ "yi" + // 0003d - 4045
+ "yi-001" + // 0043d - 4047
+ "yo" + // 0006a - 4053
+ "yo-bj" + // 01000 - 4055
+ "yo-ng" + // 0046a - 4060
+ "yue" + // 01000 - 4065
+ "yue-hk" + // 01000 - 4068
+ "zgh" + // 01000 - 4074
+ "zgh-tfng" + // 01000 - 4077
+ "zgh-tfng-ma" + // 01000 - 4085
+ "zh" + // 07804 - 4096
+ "zh-chs" + // 00004 - 4098
+ "zh-cht" + // 07c04 - 4104
+ "zh-cn" + // 00804 - 4110
+ "zh-cn_phoneb" + // 50804 - 4115
+ "zh-cn_stroke" + // 20804 - 4127
+ "zh-hans" + // 00004 - 4139
+ "zh-hans-hk" + // 01000 - 4146
+ "zh-hans-mo" + // 01000 - 4156
+ "zh-hant" + // 07c04 - 4166
+ "zh-hk" + // 00c04 - 4173
+ "zh-hk_radstr" + // 40c04 - 4178
+ "zh-mo" + // 01404 - 4190
+ "zh-mo_radstr" + // 41404 - 4195
+ "zh-mo_stroke" + // 21404 - 4207
+ "zh-sg" + // 01004 - 4219
+ "zh-sg_phoneb" + // 51004 - 4224
+ "zh-sg_stroke" + // 21004 - 4236
+ "zh-tw" + // 00404 - 4248
+ "zh-tw_pronun" + // 30404 - 4253
+ "zh-tw_radstr" + // 40404 - 4265
+ "zu" + // 00035 - 4277
+ "zu-za"; // 00435 - 4279
+
+ // c_threeLetterWindowsLanguageName is string containing 3-letter Windows language names
+ // every 3-characters entry is matching locale name entry in c_localeNames
+
+ private const string c_threeLetterWindowsLanguageName =
+ "ZZZ" + // aa
+ "ZZZ" + // aa-dj
+ "ZZZ" + // aa-er
+ "ZZZ" + // aa-et
+ "AFK" + // af
+ "ZZZ" + // af-na
+ "AFK" + // af-za
+ "ZZZ" + // agq
+ "ZZZ" + // agq-cm
+ "ZZZ" + // ak
+ "ZZZ" + // ak-gh
+ "AMH" + // am
+ "AMH" + // am-et
+ "ARA" + // ar
+ "ZZZ" + // ar-001
+ "ARU" + // ar-ae
+ "ARH" + // ar-bh
+ "ZZZ" + // ar-dj
+ "ARG" + // ar-dz
+ "ARE" + // ar-eg
+ "ZZZ" + // ar-er
+ "ZZZ" + // ar-il
+ "ARI" + // ar-iq
+ "ARJ" + // ar-jo
+ "ZZZ" + // ar-km
+ "ARK" + // ar-kw
+ "ARB" + // ar-lb
+ "ARL" + // ar-ly
+ "ARM" + // ar-ma
+ "ZZZ" + // ar-mr
+ "ARO" + // ar-om
+ "ZZZ" + // ar-ps
+ "ARQ" + // ar-qa
+ "ARA" + // ar-sa
+ "ZZZ" + // ar-sd
+ "ZZZ" + // ar-so
+ "ZZZ" + // ar-ss
+ "ARS" + // ar-sy
+ "ZZZ" + // ar-td
+ "ART" + // ar-tn
+ "ARY" + // ar-ye
+ "MPD" + // arn
+ "MPD" + // arn-cl
+ "ASM" + // as
+ "ASM" + // as-in
+ "ZZZ" + // asa
+ "ZZZ" + // asa-tz
+ "ZZZ" + // ast
+ "ZZZ" + // ast-es
+ "AZE" + // az
+ "AZC" + // az-cyrl
+ "AZC" + // az-cyrl-az
+ "AZE" + // az-latn
+ "AZE" + // az-latn-az
+ "BAS" + // ba
+ "BAS" + // ba-ru
+ "ZZZ" + // bas
+ "ZZZ" + // bas-cm
+ "BEL" + // be
+ "BEL" + // be-by
+ "ZZZ" + // bem
+ "ZZZ" + // bem-zm
+ "ZZZ" + // bez
+ "ZZZ" + // bez-tz
+ "BGR" + // bg
+ "BGR" + // bg-bg
+ "ZZZ" + // bin
+ "ZZZ" + // bin-ng
+ "ZZZ" + // bm
+ "ZZZ" + // bm-latn
+ "ZZZ" + // bm-latn-ml
+ "BNB" + // bn
+ "BNB" + // bn-bd
+ "BNG" + // bn-in
+ "BOB" + // bo
+ "BOB" + // bo-cn
+ "ZZZ" + // bo-in
+ "BRE" + // br
+ "BRE" + // br-fr
+ "ZZZ" + // brx
+ "ZZZ" + // brx-in
+ "BSB" + // bs
+ "BSC" + // bs-cyrl
+ "BSC" + // bs-cyrl-ba
+ "BSB" + // bs-latn
+ "BSB" + // bs-latn-ba
+ "ZZZ" + // byn
+ "ZZZ" + // byn-er
+ "CAT" + // ca
+ "ZZZ" + // ca-ad
+ "CAT" + // ca-es
+ "VAL" + // ca-es-valencia
+ "ZZZ" + // ca-fr
+ "ZZZ" + // ca-it
+ "ZZZ" + // ce
+ "ZZZ" + // ce-ru
+ "ZZZ" + // cgg
+ "ZZZ" + // cgg-ug
+ "CRE" + // chr
+ "CRE" + // chr-cher
+ "CRE" + // chr-cher-us
+ "COS" + // co
+ "COS" + // co-fr
+ "CSY" + // cs
+ "CSY" + // cs-cz
+ "ZZZ" + // cu
+ "ZZZ" + // cu-ru
+ "CYM" + // cy
+ "CYM" + // cy-gb
+ "DAN" + // da
+ "DAN" + // da-dk
+ "ZZZ" + // da-gl
+ "ZZZ" + // dav
+ "ZZZ" + // dav-ke
+ "DEU" + // de
+ "DEA" + // de-at
+ "ZZZ" + // de-be
+ "DES" + // de-ch
+ "DEU" + // de-de
+ "DEU" + // de-de_phoneb
+ "ZZZ" + // de-it
+ "DEC" + // de-li
+ "DEL" + // de-lu
+ "ZZZ" + // dje
+ "ZZZ" + // dje-ne
+ "DSB" + // dsb
+ "DSB" + // dsb-de
+ "ZZZ" + // dua
+ "ZZZ" + // dua-cm
+ "DIV" + // dv
+ "DIV" + // dv-mv
+ "ZZZ" + // dyo
+ "ZZZ" + // dyo-sn
+ "ZZZ" + // dz
+ "ZZZ" + // dz-bt
+ "ZZZ" + // ebu
+ "ZZZ" + // ebu-ke
+ "ZZZ" + // ee
+ "ZZZ" + // ee-gh
+ "ZZZ" + // ee-tg
+ "ELL" + // el
+ "ZZZ" + // el-cy
+ "ELL" + // el-gr
+ "ENU" + // en
+ "ZZZ" + // en-001
+ "ENB" + // en-029
+ "ZZZ" + // en-150
+ "ZZZ" + // en-ag
+ "ZZZ" + // en-ai
+ "ZZZ" + // en-as
+ "ZZZ" + // en-at
+ "ENA" + // en-au
+ "ZZZ" + // en-bb
+ "ZZZ" + // en-be
+ "ZZZ" + // en-bi
+ "ZZZ" + // en-bm
+ "ZZZ" + // en-bs
+ "ZZZ" + // en-bw
+ "ENL" + // en-bz
+ "ENC" + // en-ca
+ "ZZZ" + // en-cc
+ "ZZZ" + // en-ch
+ "ZZZ" + // en-ck
+ "ZZZ" + // en-cm
+ "ZZZ" + // en-cx
+ "ZZZ" + // en-cy
+ "ZZZ" + // en-de
+ "ZZZ" + // en-dk
+ "ZZZ" + // en-dm
+ "ZZZ" + // en-er
+ "ZZZ" + // en-fi
+ "ZZZ" + // en-fj
+ "ZZZ" + // en-fk
+ "ZZZ" + // en-fm
+ "ENG" + // en-gb
+ "ZZZ" + // en-gd
+ "ZZZ" + // en-gg
+ "ZZZ" + // en-gh
+ "ZZZ" + // en-gi
+ "ZZZ" + // en-gm
+ "ZZZ" + // en-gu
+ "ZZZ" + // en-gy
+ "ENH" + // en-hk
+ "ZZZ" + // en-id
+ "ENI" + // en-ie
+ "ZZZ" + // en-il
+ "ZZZ" + // en-im
+ "ENN" + // en-in
+ "ZZZ" + // en-io
+ "ZZZ" + // en-je
+ "ENJ" + // en-jm
+ "ZZZ" + // en-ke
+ "ZZZ" + // en-ki
+ "ZZZ" + // en-kn
+ "ZZZ" + // en-ky
+ "ZZZ" + // en-lc
+ "ZZZ" + // en-lr
+ "ZZZ" + // en-ls
+ "ZZZ" + // en-mg
+ "ZZZ" + // en-mh
+ "ZZZ" + // en-mo
+ "ZZZ" + // en-mp
+ "ZZZ" + // en-ms
+ "ZZZ" + // en-mt
+ "ZZZ" + // en-mu
+ "ZZZ" + // en-mw
+ "ENM" + // en-my
+ "ZZZ" + // en-na
+ "ZZZ" + // en-nf
+ "ZZZ" + // en-ng
+ "ZZZ" + // en-nl
+ "ZZZ" + // en-nr
+ "ZZZ" + // en-nu
+ "ENZ" + // en-nz
+ "ZZZ" + // en-pg
+ "ENP" + // en-ph
+ "ZZZ" + // en-pk
+ "ZZZ" + // en-pn
+ "ZZZ" + // en-pr
+ "ZZZ" + // en-pw
+ "ZZZ" + // en-rw
+ "ZZZ" + // en-sb
+ "ZZZ" + // en-sc
+ "ZZZ" + // en-sd
+ "ZZZ" + // en-se
+ "ENE" + // en-sg
+ "ZZZ" + // en-sh
+ "ZZZ" + // en-si
+ "ZZZ" + // en-sl
+ "ZZZ" + // en-ss
+ "ZZZ" + // en-sx
+ "ZZZ" + // en-sz
+ "ZZZ" + // en-tc
+ "ZZZ" + // en-tk
+ "ZZZ" + // en-to
+ "ENT" + // en-tt
+ "ZZZ" + // en-tv
+ "ZZZ" + // en-tz
+ "ZZZ" + // en-ug
+ "ZZZ" + // en-um
+ "ENU" + // en-us
+ "ZZZ" + // en-vc
+ "ZZZ" + // en-vg
+ "ZZZ" + // en-vi
+ "ZZZ" + // en-vu
+ "ZZZ" + // en-ws
+ "ENS" + // en-za
+ "ZZZ" + // en-zm
+ "ENW" + // en-zw
+ "ZZZ" + // eo
+ "ZZZ" + // eo-001
+ "ESN" + // es
+ "ESJ" + // es-419
+ "ESS" + // es-ar
+ "ESB" + // es-bo
+ "ZZZ" + // es-br
+ "ESL" + // es-cl
+ "ESO" + // es-co
+ "ESC" + // es-cr
+ "ESK" + // es-cu
+ "ESD" + // es-do
+ "ESF" + // es-ec
+ "ESN" + // es-es
+ "ESP" + // es-es_tradnl
+ "ZZZ" + // es-gq
+ "ESG" + // es-gt
+ "ESH" + // es-hn
+ "ESM" + // es-mx
+ "ESI" + // es-ni
+ "ESA" + // es-pa
+ "ESR" + // es-pe
+ "ZZZ" + // es-ph
+ "ESU" + // es-pr
+ "ESZ" + // es-py
+ "ESE" + // es-sv
+ "EST" + // es-us
+ "ESY" + // es-uy
+ "ESV" + // es-ve
+ "ETI" + // et
+ "ETI" + // et-ee
+ "EUQ" + // eu
+ "EUQ" + // eu-es
+ "ZZZ" + // ewo
+ "ZZZ" + // ewo-cm
+ "FAR" + // fa
+ "FAR" + // fa-ir
+ "FUL" + // ff
+ "ZZZ" + // ff-cm
+ "ZZZ" + // ff-gn
+ "FUL" + // ff-latn
+ "FUL" + // ff-latn-sn
+ "ZZZ" + // ff-mr
+ "ZZZ" + // ff-ng
+ "FIN" + // fi
+ "FIN" + // fi-fi
+ "FPO" + // fil
+ "FPO" + // fil-ph
+ "FOS" + // fo
+ "ZZZ" + // fo-dk
+ "FOS" + // fo-fo
+ "FRA" + // fr
+ "ZZZ" + // fr-029
+ "FRB" + // fr-be
+ "ZZZ" + // fr-bf
+ "ZZZ" + // fr-bi
+ "ZZZ" + // fr-bj
+ "ZZZ" + // fr-bl
+ "FRC" + // fr-ca
+ "FRD" + // fr-cd
+ "ZZZ" + // fr-cf
+ "ZZZ" + // fr-cg
+ "FRS" + // fr-ch
+ "FRI" + // fr-ci
+ "FRE" + // fr-cm
+ "ZZZ" + // fr-dj
+ "ZZZ" + // fr-dz
+ "FRA" + // fr-fr
+ "ZZZ" + // fr-ga
+ "ZZZ" + // fr-gf
+ "ZZZ" + // fr-gn
+ "ZZZ" + // fr-gp
+ "ZZZ" + // fr-gq
+ "FRH" + // fr-ht
+ "ZZZ" + // fr-km
+ "FRL" + // fr-lu
+ "FRO" + // fr-ma
+ "FRM" + // fr-mc
+ "ZZZ" + // fr-mf
+ "ZZZ" + // fr-mg
+ "FRF" + // fr-ml
+ "ZZZ" + // fr-mq
+ "ZZZ" + // fr-mr
+ "ZZZ" + // fr-mu
+ "ZZZ" + // fr-nc
+ "ZZZ" + // fr-ne
+ "ZZZ" + // fr-pf
+ "ZZZ" + // fr-pm
+ "FRR" + // fr-re
+ "ZZZ" + // fr-rw
+ "ZZZ" + // fr-sc
+ "FRN" + // fr-sn
+ "ZZZ" + // fr-sy
+ "ZZZ" + // fr-td
+ "ZZZ" + // fr-tg
+ "ZZZ" + // fr-tn
+ "ZZZ" + // fr-vu
+ "ZZZ" + // fr-wf
+ "ZZZ" + // fr-yt
+ "ZZZ" + // fur
+ "ZZZ" + // fur-it
+ "FYN" + // fy
+ "FYN" + // fy-nl
+ "IRE" + // ga
+ "IRE" + // ga-ie
+ "GLA" + // gd
+ "GLA" + // gd-gb
+ "GLC" + // gl
+ "GLC" + // gl-es
+ "GRN" + // gn
+ "GRN" + // gn-py
+ "ZZZ" + // gsw
+ "ZZZ" + // gsw-ch
+ "GSW" + // gsw-fr
+ "ZZZ" + // gsw-li
+ "GUJ" + // gu
+ "GUJ" + // gu-in
+ "ZZZ" + // guz
+ "ZZZ" + // guz-ke
+ "ZZZ" + // gv
+ "ZZZ" + // gv-im
+ "HAU" + // ha
+ "HAU" + // ha-latn
+ "ZZZ" + // ha-latn-gh
+ "ZZZ" + // ha-latn-ne
+ "HAU" + // ha-latn-ng
+ "HAW" + // haw
+ "HAW" + // haw-us
+ "HEB" + // he
+ "HEB" + // he-il
+ "HIN" + // hi
+ "HIN" + // hi-in
+ "HRV" + // hr
+ "HRB" + // hr-ba
+ "HRV" + // hr-hr
+ "HSB" + // hsb
+ "HSB" + // hsb-de
+ "HUN" + // hu
+ "HUN" + // hu-hu
+ "HUN" + // hu-hu_technl
+ "HYE" + // hy
+ "HYE" + // hy-am
+ "ZZZ" + // ia
+ "ZZZ" + // ia-001
+ "ZZZ" + // ia-fr
+ "ZZZ" + // ibb
+ "ZZZ" + // ibb-ng
+ "IND" + // id
+ "IND" + // id-id
+ "IBO" + // ig
+ "IBO" + // ig-ng
+ "III" + // ii
+ "III" + // ii-cn
+ "ISL" + // is
+ "ISL" + // is-is
+ "ITA" + // it
+ "ITS" + // it-ch
+ "ITA" + // it-it
+ "ZZZ" + // it-sm
+ "IUK" + // iu
+ "IUS" + // iu-cans
+ "IUS" + // iu-cans-ca
+ "IUK" + // iu-latn
+ "IUK" + // iu-latn-ca
+ "JPN" + // ja
+ "JPN" + // ja-jp
+ "JPN" + // ja-jp_radstr
+ "ZZZ" + // jgo
+ "ZZZ" + // jgo-cm
+ "ZZZ" + // jmc
+ "ZZZ" + // jmc-tz
+ "JAV" + // jv
+ "ZZZ" + // jv-java
+ "ZZZ" + // jv-java-id
+ "JAV" + // jv-latn
+ "JAV" + // jv-latn-id
+ "KAT" + // ka
+ "KAT" + // ka-ge
+ "KAT" + // ka-ge_modern
+ "ZZZ" + // kab
+ "ZZZ" + // kab-dz
+ "ZZZ" + // kam
+ "ZZZ" + // kam-ke
+ "ZZZ" + // kde
+ "ZZZ" + // kde-tz
+ "ZZZ" + // kea
+ "ZZZ" + // kea-cv
+ "ZZZ" + // khq
+ "ZZZ" + // khq-ml
+ "ZZZ" + // ki
+ "ZZZ" + // ki-ke
+ "KKZ" + // kk
+ "KKZ" + // kk-kz
+ "ZZZ" + // kkj
+ "ZZZ" + // kkj-cm
+ "KAL" + // kl
+ "KAL" + // kl-gl
+ "ZZZ" + // kln
+ "ZZZ" + // kln-ke
+ "KHM" + // km
+ "KHM" + // km-kh
+ "KDI" + // kn
+ "KDI" + // kn-in
+ "KOR" + // ko
+ "ZZZ" + // ko-kp
+ "KOR" + // ko-kr
+ "KNK" + // kok
+ "KNK" + // kok-in
+ "ZZZ" + // kr
+ "ZZZ" + // kr-ng
+ "ZZZ" + // ks
+ "ZZZ" + // ks-arab
+ "ZZZ" + // ks-arab-in
+ "ZZZ" + // ks-deva
+ "ZZZ" + // ks-deva-in
+ "ZZZ" + // ksb
+ "ZZZ" + // ksb-tz
+ "ZZZ" + // ksf
+ "ZZZ" + // ksf-cm
+ "ZZZ" + // ksh
+ "ZZZ" + // ksh-de
+ "KUR" + // ku
+ "KUR" + // ku-arab
+ "KUR" + // ku-arab-iq
+ "ZZZ" + // ku-arab-ir
+ "ZZZ" + // kw
+ "ZZZ" + // kw-gb
+ "KYR" + // ky
+ "KYR" + // ky-kg
+ "ZZZ" + // la
+ "ZZZ" + // la-001
+ "ZZZ" + // lag
+ "ZZZ" + // lag-tz
+ "LBX" + // lb
+ "LBX" + // lb-lu
+ "ZZZ" + // lg
+ "ZZZ" + // lg-ug
+ "ZZZ" + // lkt
+ "ZZZ" + // lkt-us
+ "ZZZ" + // ln
+ "ZZZ" + // ln-ao
+ "ZZZ" + // ln-cd
+ "ZZZ" + // ln-cf
+ "ZZZ" + // ln-cg
+ "LAO" + // lo
+ "LAO" + // lo-la
+ "ZZZ" + // lrc
+ "ZZZ" + // lrc-iq
+ "ZZZ" + // lrc-ir
+ "LTH" + // lt
+ "LTH" + // lt-lt
+ "ZZZ" + // lu
+ "ZZZ" + // lu-cd
+ "ZZZ" + // luo
+ "ZZZ" + // luo-ke
+ "ZZZ" + // luy
+ "ZZZ" + // luy-ke
+ "LVI" + // lv
+ "LVI" + // lv-lv
+ "ZZZ" + // mas
+ "ZZZ" + // mas-ke
+ "ZZZ" + // mas-tz
+ "ZZZ" + // mer
+ "ZZZ" + // mer-ke
+ "ZZZ" + // mfe
+ "ZZZ" + // mfe-mu
+ "MLG" + // mg
+ "MLG" + // mg-mg
+ "ZZZ" + // mgh
+ "ZZZ" + // mgh-mz
+ "ZZZ" + // mgo
+ "ZZZ" + // mgo-cm
+ "MRI" + // mi
+ "MRI" + // mi-nz
+ "MKI" + // mk
+ "MKI" + // mk-mk
+ "MYM" + // ml
+ "MYM" + // ml-in
+ "MNN" + // mn
+ "MNN" + // mn-cyrl
+ "MNN" + // mn-mn
+ "MNG" + // mn-mong
+ "MNG" + // mn-mong-cn
+ "MNM" + // mn-mong-mn
+ "ZZZ" + // mni
+ "ZZZ" + // mni-in
+ "MWK" + // moh
+ "MWK" + // moh-ca
+ "MAR" + // mr
+ "MAR" + // mr-in
+ "MSL" + // ms
+ "MSB" + // ms-bn
+ "MSL" + // ms-my
+ "ZZZ" + // ms-sg
+ "MLT" + // mt
+ "MLT" + // mt-mt
+ "ZZZ" + // mua
+ "ZZZ" + // mua-cm
+ "MYA" + // my
+ "MYA" + // my-mm
+ "ZZZ" + // mzn
+ "ZZZ" + // mzn-ir
+ "ZZZ" + // naq
+ "ZZZ" + // naq-na
+ "NOR" + // nb
+ "NOR" + // nb-no
+ "ZZZ" + // nb-sj
+ "ZZZ" + // nd
+ "ZZZ" + // nd-zw
+ "ZZZ" + // nds
+ "ZZZ" + // nds-de
+ "ZZZ" + // nds-nl
+ "NEP" + // ne
+ "NEI" + // ne-in
+ "NEP" + // ne-np
+ "NLD" + // nl
+ "ZZZ" + // nl-aw
+ "NLB" + // nl-be
+ "ZZZ" + // nl-bq
+ "ZZZ" + // nl-cw
+ "NLD" + // nl-nl
+ "ZZZ" + // nl-sr
+ "ZZZ" + // nl-sx
+ "ZZZ" + // nmg
+ "ZZZ" + // nmg-cm
+ "NON" + // nn
+ "NON" + // nn-no
+ "ZZZ" + // nnh
+ "ZZZ" + // nnh-cm
+ "NOR" + // no
+ "NQO" + // nqo
+ "NQO" + // nqo-gn
+ "ZZZ" + // nr
+ "ZZZ" + // nr-za
+ "NSO" + // nso
+ "NSO" + // nso-za
+ "ZZZ" + // nus
+ "ZZZ" + // nus-ss
+ "ZZZ" + // nyn
+ "ZZZ" + // nyn-ug
+ "OCI" + // oc
+ "OCI" + // oc-fr
+ "ORM" + // om
+ "ORM" + // om-et
+ "ZZZ" + // om-ke
+ "ORI" + // or
+ "ORI" + // or-in
+ "ZZZ" + // os
+ "ZZZ" + // os-ge
+ "ZZZ" + // os-ru
+ "PAN" + // pa
+ "PAP" + // pa-arab
+ "PAP" + // pa-arab-pk
+ "PAN" + // pa-in
+ "ZZZ" + // pap
+ "ZZZ" + // pap-029
+ "PLK" + // pl
+ "PLK" + // pl-pl
+ "ZZZ" + // prg
+ "ZZZ" + // prg-001
+ "PRS" + // prs
+ "PRS" + // prs-af
+ "PAS" + // ps
+ "PAS" + // ps-af
+ "PTB" + // pt
+ "PTA" + // pt-ao
+ "PTB" + // pt-br
+ "ZZZ" + // pt-ch
+ "ZZZ" + // pt-cv
+ "ZZZ" + // pt-gq
+ "ZZZ" + // pt-gw
+ "ZZZ" + // pt-lu
+ "ZZZ" + // pt-mo
+ "ZZZ" + // pt-mz
+ "PTG" + // pt-pt
+ "ZZZ" + // pt-st
+ "ZZZ" + // pt-tl
+ "ENJ" + // qps-latn-x-sh
+ "ENU" + // qps-ploc
+ "JPN" + // qps-ploca
+ "ARA" + // qps-plocm
+ "QUT" + // quc
+ "QUT" + // quc-latn
+ "QUT" + // quc-latn-gt
+ "QUB" + // quz
+ "QUB" + // quz-bo
+ "QUE" + // quz-ec
+ "QUP" + // quz-pe
+ "RMC" + // rm
+ "RMC" + // rm-ch
+ "ZZZ" + // rn
+ "ZZZ" + // rn-bi
+ "ROM" + // ro
+ "ROD" + // ro-md
+ "ROM" + // ro-ro
+ "ZZZ" + // rof
+ "ZZZ" + // rof-tz
+ "RUS" + // ru
+ "ZZZ" + // ru-by
+ "ZZZ" + // ru-kg
+ "ZZZ" + // ru-kz
+ "RUM" + // ru-md
+ "RUS" + // ru-ru
+ "ZZZ" + // ru-ua
+ "KIN" + // rw
+ "KIN" + // rw-rw
+ "ZZZ" + // rwk
+ "ZZZ" + // rwk-tz
+ "SAN" + // sa
+ "SAN" + // sa-in
+ "SAH" + // sah
+ "SAH" + // sah-ru
+ "ZZZ" + // saq
+ "ZZZ" + // saq-ke
+ "ZZZ" + // sbp
+ "ZZZ" + // sbp-tz
+ "SIP" + // sd
+ "SIP" + // sd-arab
+ "SIP" + // sd-arab-pk
+ "ZZZ" + // sd-deva
+ "ZZZ" + // sd-deva-in
+ "SME" + // se
+ "SMG" + // se-fi
+ "SME" + // se-no
+ "SMF" + // se-se
+ "ZZZ" + // seh
+ "ZZZ" + // seh-mz
+ "ZZZ" + // ses
+ "ZZZ" + // ses-ml
+ "ZZZ" + // sg
+ "ZZZ" + // sg-cf
+ "ZZZ" + // shi
+ "ZZZ" + // shi-latn
+ "ZZZ" + // shi-latn-ma
+ "ZZZ" + // shi-tfng
+ "ZZZ" + // shi-tfng-ma
+ "SIN" + // si
+ "SIN" + // si-lk
+ "SKY" + // sk
+ "SKY" + // sk-sk
+ "SLV" + // sl
+ "SLV" + // sl-si
+ "SMB" + // sma
+ "SMA" + // sma-no
+ "SMB" + // sma-se
+ "SMK" + // smj
+ "SMJ" + // smj-no
+ "SMK" + // smj-se
+ "SMN" + // smn
+ "SMN" + // smn-fi
+ "SMS" + // sms
+ "SMS" + // sms-fi
+ "SNA" + // sn
+ "SNA" + // sn-latn
+ "SNA" + // sn-latn-zw
+ "SOM" + // so
+ "ZZZ" + // so-dj
+ "ZZZ" + // so-et
+ "ZZZ" + // so-ke
+ "SOM" + // so-so
+ "SQI" + // sq
+ "SQI" + // sq-al
+ "ZZZ" + // sq-mk
+ "ZZZ" + // sq-xk
+ "SRM" + // sr
+ "SRO" + // sr-cyrl
+ "SRN" + // sr-cyrl-ba
+ "SRB" + // sr-cyrl-cs
+ "SRQ" + // sr-cyrl-me
+ "SRO" + // sr-cyrl-rs
+ "ZZZ" + // sr-cyrl-xk
+ "SRM" + // sr-latn
+ "SRS" + // sr-latn-ba
+ "SRL" + // sr-latn-cs
+ "SRP" + // sr-latn-me
+ "SRM" + // sr-latn-rs
+ "ZZZ" + // sr-latn-xk
+ "ZZZ" + // ss
+ "ZZZ" + // ss-sz
+ "ZZZ" + // ss-za
+ "ZZZ" + // ssy
+ "ZZZ" + // ssy-er
+ "SOT" + // st
+ "ZZZ" + // st-ls
+ "SOT" + // st-za
+ "SVE" + // sv
+ "ZZZ" + // sv-ax
+ "SVF" + // sv-fi
+ "SVE" + // sv-se
+ "SWK" + // sw
+ "ZZZ" + // sw-cd
+ "SWK" + // sw-ke
+ "ZZZ" + // sw-tz
+ "ZZZ" + // sw-ug
+ "ZZZ" + // swc
+ "ZZZ" + // swc-cd
+ "SYR" + // syr
+ "SYR" + // syr-sy
+ "TAI" + // ta
+ "TAI" + // ta-in
+ "TAM" + // ta-lk
+ "ZZZ" + // ta-my
+ "ZZZ" + // ta-sg
+ "TEL" + // te
+ "TEL" + // te-in
+ "ZZZ" + // teo
+ "ZZZ" + // teo-ke
+ "ZZZ" + // teo-ug
+ "TAJ" + // tg
+ "TAJ" + // tg-cyrl
+ "TAJ" + // tg-cyrl-tj
+ "THA" + // th
+ "THA" + // th-th
+ "TIR" + // ti
+ "TIR" + // ti-er
+ "TIE" + // ti-et
+ "ZZZ" + // tig
+ "ZZZ" + // tig-er
+ "TUK" + // tk
+ "TUK" + // tk-tm
+ "TSN" + // tn
+ "TSB" + // tn-bw
+ "TSN" + // tn-za
+ "ZZZ" + // to
+ "ZZZ" + // to-to
+ "TRK" + // tr
+ "ZZZ" + // tr-cy
+ "TRK" + // tr-tr
+ "TSO" + // ts
+ "TSO" + // ts-za
+ "TTT" + // tt
+ "TTT" + // tt-ru
+ "ZZZ" + // twq
+ "ZZZ" + // twq-ne
+ "TZA" + // tzm
+ "ZZZ" + // tzm-arab
+ "ZZZ" + // tzm-arab-ma
+ "TZA" + // tzm-latn
+ "TZA" + // tzm-latn-dz
+ "ZZZ" + // tzm-latn-ma
+ "TZM" + // tzm-tfng
+ "TZM" + // tzm-tfng-ma
+ "UIG" + // ug
+ "UIG" + // ug-cn
+ "UKR" + // uk
+ "UKR" + // uk-ua
+ "URD" + // ur
+ "URI" + // ur-in
+ "URD" + // ur-pk
+ "UZB" + // uz
+ "ZZZ" + // uz-arab
+ "ZZZ" + // uz-arab-af
+ "UZC" + // uz-cyrl
+ "UZC" + // uz-cyrl-uz
+ "UZB" + // uz-latn
+ "UZB" + // uz-latn-uz
+ "ZZZ" + // vai
+ "ZZZ" + // vai-latn
+ "ZZZ" + // vai-latn-lr
+ "ZZZ" + // vai-vaii
+ "ZZZ" + // vai-vaii-lr
+ "ZZZ" + // ve
+ "ZZZ" + // ve-za
+ "VIT" + // vi
+ "VIT" + // vi-vn
+ "ZZZ" + // vo
+ "ZZZ" + // vo-001
+ "ZZZ" + // vun
+ "ZZZ" + // vun-tz
+ "ZZZ" + // wae
+ "ZZZ" + // wae-ch
+ "ZZZ" + // wal
+ "ZZZ" + // wal-et
+ "WOL" + // wo
+ "WOL" + // wo-sn
+ "IVL" + // x-iv_mathan
+ "XHO" + // xh
+ "XHO" + // xh-za
+ "ZZZ" + // xog
+ "ZZZ" + // xog-ug
+ "ZZZ" + // yav
+ "ZZZ" + // yav-cm
+ "ZZZ" + // yi
+ "ZZZ" + // yi-001
+ "YOR" + // yo
+ "ZZZ" + // yo-bj
+ "YOR" + // yo-ng
+ "ZZZ" + // yue
+ "ZZZ" + // yue-hk
+ "ZHG" + // zgh
+ "ZHG" + // zgh-tfng
+ "ZHG" + // zgh-tfng-ma
+ "CHS" + // zh
+ "CHS" + // zh-chs
+ "CHT" + // zh-cht
+ "CHS" + // zh-cn
+ "CHS" + // zh-cn_phoneb
+ "CHS" + // zh-cn_stroke
+ "CHS" + // zh-hans
+ "ZZZ" + // zh-hans-hk
+ "ZZZ" + // zh-hans-mo
+ "ZHH" + // zh-hant
+ "ZHH" + // zh-hk
+ "ZHH" + // zh-hk_radstr
+ "ZHM" + // zh-mo
+ "ZHM" + // zh-mo_radstr
+ "ZHM" + // zh-mo_stroke
+ "ZHI" + // zh-sg
+ "ZHI" + // zh-sg_phoneb
+ "ZHI" + // zh-sg_stroke
+ "CHT" + // zh-tw
+ "CHT" + // zh-tw_pronun
+ "CHT" + // zh-tw_radstr
+ "ZUL" + // zu
+ "ZUL"; // zu-za
+
+ // s_localeNamesIndices contains the start index of every culture name in the string
+ // s_localeNames. We infer the length of each string by looking at the start index
+ // of the next string.
+ private static readonly int[] s_localeNamesIndices = new int[]
+ {
+ // c_localeNames index, // index to this array - culture name
+ 0 , // 0 - aa
+ 2 , // 1 - aa-dj
+ 7 , // 2 - aa-er
+ 12 , // 3 - aa-et
+ 17 , // 4 - af
+ 19 , // 5 - af-na
+ 24 , // 6 - af-za
+ 29 , // 7 - agq
+ 32 , // 8 - agq-cm
+ 38 , // 9 - ak
+ 40 , // 10 - ak-gh
+ 45 , // 11 - am
+ 47 , // 12 - am-et
+ 52 , // 13 - ar
+ 54 , // 14 - ar-001
+ 60 , // 15 - ar-ae
+ 65 , // 16 - ar-bh
+ 70 , // 17 - ar-dj
+ 75 , // 18 - ar-dz
+ 80 , // 19 - ar-eg
+ 85 , // 20 - ar-er
+ 90 , // 21 - ar-il
+ 95 , // 22 - ar-iq
+ 100 , // 23 - ar-jo
+ 105 , // 24 - ar-km
+ 110 , // 25 - ar-kw
+ 115 , // 26 - ar-lb
+ 120 , // 27 - ar-ly
+ 125 , // 28 - ar-ma
+ 130 , // 29 - ar-mr
+ 135 , // 30 - ar-om
+ 140 , // 31 - ar-ps
+ 145 , // 32 - ar-qa
+ 150 , // 33 - ar-sa
+ 155 , // 34 - ar-sd
+ 160 , // 35 - ar-so
+ 165 , // 36 - ar-ss
+ 170 , // 37 - ar-sy
+ 175 , // 38 - ar-td
+ 180 , // 39 - ar-tn
+ 185 , // 40 - ar-ye
+ 190 , // 41 - arn
+ 193 , // 42 - arn-cl
+ 199 , // 43 - as
+ 201 , // 44 - as-in
+ 206 , // 45 - asa
+ 209 , // 46 - asa-tz
+ 215 , // 47 - ast
+ 218 , // 48 - ast-es
+ 224 , // 49 - az
+ 226 , // 50 - az-cyrl
+ 233 , // 51 - az-cyrl-az
+ 243 , // 52 - az-latn
+ 250 , // 53 - az-latn-az
+ 260 , // 54 - ba
+ 262 , // 55 - ba-ru
+ 267 , // 56 - bas
+ 270 , // 57 - bas-cm
+ 276 , // 58 - be
+ 278 , // 59 - be-by
+ 283 , // 60 - bem
+ 286 , // 61 - bem-zm
+ 292 , // 62 - bez
+ 295 , // 63 - bez-tz
+ 301 , // 64 - bg
+ 303 , // 65 - bg-bg
+ 308 , // 66 - bin
+ 311 , // 67 - bin-ng
+ 317 , // 68 - bm
+ 319 , // 69 - bm-latn
+ 326 , // 70 - bm-latn-ml
+ 336 , // 71 - bn
+ 338 , // 72 - bn-bd
+ 343 , // 73 - bn-in
+ 348 , // 74 - bo
+ 350 , // 75 - bo-cn
+ 355 , // 76 - bo-in
+ 360 , // 77 - br
+ 362 , // 78 - br-fr
+ 367 , // 79 - brx
+ 370 , // 80 - brx-in
+ 376 , // 81 - bs
+ 378 , // 82 - bs-cyrl
+ 385 , // 83 - bs-cyrl-ba
+ 395 , // 84 - bs-latn
+ 402 , // 85 - bs-latn-ba
+ 412 , // 86 - byn
+ 415 , // 87 - byn-er
+ 421 , // 88 - ca
+ 423 , // 89 - ca-ad
+ 428 , // 90 - ca-es
+ 433 , // 91 - ca-es-valencia
+ 447 , // 92 - ca-fr
+ 452 , // 93 - ca-it
+ 457 , // 94 - ce
+ 459 , // 95 - ce-ru
+ 464 , // 96 - cgg
+ 467 , // 97 - cgg-ug
+ 473 , // 98 - chr
+ 476 , // 99 - chr-cher
+ 484 , // 100 - chr-cher-us
+ 495 , // 101 - co
+ 497 , // 102 - co-fr
+ 502 , // 103 - cs
+ 504 , // 104 - cs-cz
+ 509 , // 105 - cu
+ 511 , // 106 - cu-ru
+ 516 , // 107 - cy
+ 518 , // 108 - cy-gb
+ 523 , // 109 - da
+ 525 , // 110 - da-dk
+ 530 , // 111 - da-gl
+ 535 , // 112 - dav
+ 538 , // 113 - dav-ke
+ 544 , // 114 - de
+ 546 , // 115 - de-at
+ 551 , // 116 - de-be
+ 556 , // 117 - de-ch
+ 561 , // 118 - de-de
+ 566 , // 119 - de-de_phoneb
+ 578 , // 120 - de-it
+ 583 , // 121 - de-li
+ 588 , // 122 - de-lu
+ 593 , // 123 - dje
+ 596 , // 124 - dje-ne
+ 602 , // 125 - dsb
+ 605 , // 126 - dsb-de
+ 611 , // 127 - dua
+ 614 , // 128 - dua-cm
+ 620 , // 129 - dv
+ 622 , // 130 - dv-mv
+ 627 , // 131 - dyo
+ 630 , // 132 - dyo-sn
+ 636 , // 133 - dz
+ 638 , // 134 - dz-bt
+ 643 , // 135 - ebu
+ 646 , // 136 - ebu-ke
+ 652 , // 137 - ee
+ 654 , // 138 - ee-gh
+ 659 , // 139 - ee-tg
+ 664 , // 140 - el
+ 666 , // 141 - el-cy
+ 671 , // 142 - el-gr
+ 676 , // 143 - en
+ 678 , // 144 - en-001
+ 684 , // 145 - en-029
+ 690 , // 146 - en-150
+ 696 , // 147 - en-ag
+ 701 , // 148 - en-ai
+ 706 , // 149 - en-as
+ 711 , // 150 - en-at
+ 716 , // 151 - en-au
+ 721 , // 152 - en-bb
+ 726 , // 153 - en-be
+ 731 , // 154 - en-bi
+ 736 , // 155 - en-bm
+ 741 , // 156 - en-bs
+ 746 , // 157 - en-bw
+ 751 , // 158 - en-bz
+ 756 , // 159 - en-ca
+ 761 , // 160 - en-cc
+ 766 , // 161 - en-ch
+ 771 , // 162 - en-ck
+ 776 , // 163 - en-cm
+ 781 , // 164 - en-cx
+ 786 , // 165 - en-cy
+ 791 , // 166 - en-de
+ 796 , // 167 - en-dk
+ 801 , // 168 - en-dm
+ 806 , // 169 - en-er
+ 811 , // 170 - en-fi
+ 816 , // 171 - en-fj
+ 821 , // 172 - en-fk
+ 826 , // 173 - en-fm
+ 831 , // 174 - en-gb
+ 836 , // 175 - en-gd
+ 841 , // 176 - en-gg
+ 846 , // 177 - en-gh
+ 851 , // 178 - en-gi
+ 856 , // 179 - en-gm
+ 861 , // 180 - en-gu
+ 866 , // 181 - en-gy
+ 871 , // 182 - en-hk
+ 876 , // 183 - en-id
+ 881 , // 184 - en-ie
+ 886 , // 185 - en-il
+ 891 , // 186 - en-im
+ 896 , // 187 - en-in
+ 901 , // 188 - en-io
+ 906 , // 189 - en-je
+ 911 , // 190 - en-jm
+ 916 , // 191 - en-ke
+ 921 , // 192 - en-ki
+ 926 , // 193 - en-kn
+ 931 , // 194 - en-ky
+ 936 , // 195 - en-lc
+ 941 , // 196 - en-lr
+ 946 , // 197 - en-ls
+ 951 , // 198 - en-mg
+ 956 , // 199 - en-mh
+ 961 , // 200 - en-mo
+ 966 , // 201 - en-mp
+ 971 , // 202 - en-ms
+ 976 , // 203 - en-mt
+ 981 , // 204 - en-mu
+ 986 , // 205 - en-mw
+ 991 , // 206 - en-my
+ 996 , // 207 - en-na
+ 1001, // 208 - en-nf
+ 1006, // 209 - en-ng
+ 1011, // 210 - en-nl
+ 1016, // 211 - en-nr
+ 1021, // 212 - en-nu
+ 1026, // 213 - en-nz
+ 1031, // 214 - en-pg
+ 1036, // 215 - en-ph
+ 1041, // 216 - en-pk
+ 1046, // 217 - en-pn
+ 1051, // 218 - en-pr
+ 1056, // 219 - en-pw
+ 1061, // 220 - en-rw
+ 1066, // 221 - en-sb
+ 1071, // 222 - en-sc
+ 1076, // 223 - en-sd
+ 1081, // 224 - en-se
+ 1086, // 225 - en-sg
+ 1091, // 226 - en-sh
+ 1096, // 227 - en-si
+ 1101, // 228 - en-sl
+ 1106, // 229 - en-ss
+ 1111, // 230 - en-sx
+ 1116, // 231 - en-sz
+ 1121, // 232 - en-tc
+ 1126, // 233 - en-tk
+ 1131, // 234 - en-to
+ 1136, // 235 - en-tt
+ 1141, // 236 - en-tv
+ 1146, // 237 - en-tz
+ 1151, // 238 - en-ug
+ 1156, // 239 - en-um
+ 1161, // 240 - en-us
+ 1166, // 241 - en-vc
+ 1171, // 242 - en-vg
+ 1176, // 243 - en-vi
+ 1181, // 244 - en-vu
+ 1186, // 245 - en-ws
+ 1191, // 246 - en-za
+ 1196, // 247 - en-zm
+ 1201, // 248 - en-zw
+ 1206, // 249 - eo
+ 1208, // 250 - eo-001
+ 1214, // 251 - es
+ 1216, // 252 - es-419
+ 1222, // 253 - es-ar
+ 1227, // 254 - es-bo
+ 1232, // 255 - es-br
+ 1237, // 256 - es-cl
+ 1242, // 257 - es-co
+ 1247, // 258 - es-cr
+ 1252, // 259 - es-cu
+ 1257, // 260 - es-do
+ 1262, // 261 - es-ec
+ 1267, // 262 - es-es
+ 1272, // 263 - es-es_tradnl
+ 1284, // 264 - es-gq
+ 1289, // 265 - es-gt
+ 1294, // 266 - es-hn
+ 1299, // 267 - es-mx
+ 1304, // 268 - es-ni
+ 1309, // 269 - es-pa
+ 1314, // 270 - es-pe
+ 1319, // 271 - es-ph
+ 1324, // 272 - es-pr
+ 1329, // 273 - es-py
+ 1334, // 274 - es-sv
+ 1339, // 275 - es-us
+ 1344, // 276 - es-uy
+ 1349, // 277 - es-ve
+ 1354, // 278 - et
+ 1356, // 279 - et-ee
+ 1361, // 280 - eu
+ 1363, // 281 - eu-es
+ 1368, // 282 - ewo
+ 1371, // 283 - ewo-cm
+ 1377, // 284 - fa
+ 1379, // 285 - fa-ir
+ 1384, // 286 - ff
+ 1386, // 287 - ff-cm
+ 1391, // 288 - ff-gn
+ 1396, // 289 - ff-latn
+ 1403, // 290 - ff-latn-sn
+ 1413, // 291 - ff-mr
+ 1418, // 292 - ff-ng
+ 1423, // 293 - fi
+ 1425, // 294 - fi-fi
+ 1430, // 295 - fil
+ 1433, // 296 - fil-ph
+ 1439, // 297 - fo
+ 1441, // 298 - fo-dk
+ 1446, // 299 - fo-fo
+ 1451, // 300 - fr
+ 1453, // 301 - fr-029
+ 1459, // 302 - fr-be
+ 1464, // 303 - fr-bf
+ 1469, // 304 - fr-bi
+ 1474, // 305 - fr-bj
+ 1479, // 306 - fr-bl
+ 1484, // 307 - fr-ca
+ 1489, // 308 - fr-cd
+ 1494, // 309 - fr-cf
+ 1499, // 310 - fr-cg
+ 1504, // 311 - fr-ch
+ 1509, // 312 - fr-ci
+ 1514, // 313 - fr-cm
+ 1519, // 314 - fr-dj
+ 1524, // 315 - fr-dz
+ 1529, // 316 - fr-fr
+ 1534, // 317 - fr-ga
+ 1539, // 318 - fr-gf
+ 1544, // 319 - fr-gn
+ 1549, // 320 - fr-gp
+ 1554, // 321 - fr-gq
+ 1559, // 322 - fr-ht
+ 1564, // 323 - fr-km
+ 1569, // 324 - fr-lu
+ 1574, // 325 - fr-ma
+ 1579, // 326 - fr-mc
+ 1584, // 327 - fr-mf
+ 1589, // 328 - fr-mg
+ 1594, // 329 - fr-ml
+ 1599, // 330 - fr-mq
+ 1604, // 331 - fr-mr
+ 1609, // 332 - fr-mu
+ 1614, // 333 - fr-nc
+ 1619, // 334 - fr-ne
+ 1624, // 335 - fr-pf
+ 1629, // 336 - fr-pm
+ 1634, // 337 - fr-re
+ 1639, // 338 - fr-rw
+ 1644, // 339 - fr-sc
+ 1649, // 340 - fr-sn
+ 1654, // 341 - fr-sy
+ 1659, // 342 - fr-td
+ 1664, // 343 - fr-tg
+ 1669, // 344 - fr-tn
+ 1674, // 345 - fr-vu
+ 1679, // 346 - fr-wf
+ 1684, // 347 - fr-yt
+ 1689, // 348 - fur
+ 1692, // 349 - fur-it
+ 1698, // 350 - fy
+ 1700, // 351 - fy-nl
+ 1705, // 352 - ga
+ 1707, // 353 - ga-ie
+ 1712, // 354 - gd
+ 1714, // 355 - gd-gb
+ 1719, // 356 - gl
+ 1721, // 357 - gl-es
+ 1726, // 358 - gn
+ 1728, // 359 - gn-py
+ 1733, // 360 - gsw
+ 1736, // 361 - gsw-ch
+ 1742, // 362 - gsw-fr
+ 1748, // 363 - gsw-li
+ 1754, // 364 - gu
+ 1756, // 365 - gu-in
+ 1761, // 366 - guz
+ 1764, // 367 - guz-ke
+ 1770, // 368 - gv
+ 1772, // 369 - gv-im
+ 1777, // 370 - ha
+ 1779, // 371 - ha-latn
+ 1786, // 372 - ha-latn-gh
+ 1796, // 373 - ha-latn-ne
+ 1806, // 374 - ha-latn-ng
+ 1816, // 375 - haw
+ 1819, // 376 - haw-us
+ 1825, // 377 - he
+ 1827, // 378 - he-il
+ 1832, // 379 - hi
+ 1834, // 380 - hi-in
+ 1839, // 381 - hr
+ 1841, // 382 - hr-ba
+ 1846, // 383 - hr-hr
+ 1851, // 384 - hsb
+ 1854, // 385 - hsb-de
+ 1860, // 386 - hu
+ 1862, // 387 - hu-hu
+ 1867, // 388 - hu-hu_technl
+ 1879, // 389 - hy
+ 1881, // 390 - hy-am
+ 1886, // 391 - ia
+ 1888, // 392 - ia-001
+ 1894, // 393 - ia-fr
+ 1899, // 394 - ibb
+ 1902, // 395 - ibb-ng
+ 1908, // 396 - id
+ 1910, // 397 - id-id
+ 1915, // 398 - ig
+ 1917, // 399 - ig-ng
+ 1922, // 400 - ii
+ 1924, // 401 - ii-cn
+ 1929, // 402 - is
+ 1931, // 403 - is-is
+ 1936, // 404 - it
+ 1938, // 405 - it-ch
+ 1943, // 406 - it-it
+ 1948, // 407 - it-sm
+ 1953, // 408 - iu
+ 1955, // 409 - iu-cans
+ 1962, // 410 - iu-cans-ca
+ 1972, // 411 - iu-latn
+ 1979, // 412 - iu-latn-ca
+ 1989, // 413 - ja
+ 1991, // 414 - ja-jp
+ 1996, // 415 - ja-jp_radstr
+ 2008, // 416 - jgo
+ 2011, // 417 - jgo-cm
+ 2017, // 418 - jmc
+ 2020, // 419 - jmc-tz
+ 2026, // 420 - jv
+ 2028, // 421 - jv-java
+ 2035, // 422 - jv-java-id
+ 2045, // 423 - jv-latn
+ 2052, // 424 - jv-latn-id
+ 2062, // 425 - ka
+ 2064, // 426 - ka-ge
+ 2069, // 427 - ka-ge_modern
+ 2081, // 428 - kab
+ 2084, // 429 - kab-dz
+ 2090, // 430 - kam
+ 2093, // 431 - kam-ke
+ 2099, // 432 - kde
+ 2102, // 433 - kde-tz
+ 2108, // 434 - kea
+ 2111, // 435 - kea-cv
+ 2117, // 436 - khq
+ 2120, // 437 - khq-ml
+ 2126, // 438 - ki
+ 2128, // 439 - ki-ke
+ 2133, // 440 - kk
+ 2135, // 441 - kk-kz
+ 2140, // 442 - kkj
+ 2143, // 443 - kkj-cm
+ 2149, // 444 - kl
+ 2151, // 445 - kl-gl
+ 2156, // 446 - kln
+ 2159, // 447 - kln-ke
+ 2165, // 448 - km
+ 2167, // 449 - km-kh
+ 2172, // 450 - kn
+ 2174, // 451 - kn-in
+ 2179, // 452 - ko
+ 2181, // 453 - ko-kp
+ 2186, // 454 - ko-kr
+ 2191, // 455 - kok
+ 2194, // 456 - kok-in
+ 2200, // 457 - kr
+ 2202, // 458 - kr-ng
+ 2207, // 459 - ks
+ 2209, // 460 - ks-arab
+ 2216, // 461 - ks-arab-in
+ 2226, // 462 - ks-deva
+ 2233, // 463 - ks-deva-in
+ 2243, // 464 - ksb
+ 2246, // 465 - ksb-tz
+ 2252, // 466 - ksf
+ 2255, // 467 - ksf-cm
+ 2261, // 468 - ksh
+ 2264, // 469 - ksh-de
+ 2270, // 470 - ku
+ 2272, // 471 - ku-arab
+ 2279, // 472 - ku-arab-iq
+ 2289, // 473 - ku-arab-ir
+ 2299, // 474 - kw
+ 2301, // 475 - kw-gb
+ 2306, // 476 - ky
+ 2308, // 477 - ky-kg
+ 2313, // 478 - la
+ 2315, // 479 - la-001
+ 2321, // 480 - lag
+ 2324, // 481 - lag-tz
+ 2330, // 482 - lb
+ 2332, // 483 - lb-lu
+ 2337, // 484 - lg
+ 2339, // 485 - lg-ug
+ 2344, // 486 - lkt
+ 2347, // 487 - lkt-us
+ 2353, // 488 - ln
+ 2355, // 489 - ln-ao
+ 2360, // 490 - ln-cd
+ 2365, // 491 - ln-cf
+ 2370, // 492 - ln-cg
+ 2375, // 493 - lo
+ 2377, // 494 - lo-la
+ 2382, // 495 - lrc
+ 2385, // 496 - lrc-iq
+ 2391, // 497 - lrc-ir
+ 2397, // 498 - lt
+ 2399, // 499 - lt-lt
+ 2404, // 500 - lu
+ 2406, // 501 - lu-cd
+ 2411, // 502 - luo
+ 2414, // 503 - luo-ke
+ 2420, // 504 - luy
+ 2423, // 505 - luy-ke
+ 2429, // 506 - lv
+ 2431, // 507 - lv-lv
+ 2436, // 508 - mas
+ 2439, // 509 - mas-ke
+ 2445, // 510 - mas-tz
+ 2451, // 511 - mer
+ 2454, // 512 - mer-ke
+ 2460, // 513 - mfe
+ 2463, // 514 - mfe-mu
+ 2469, // 515 - mg
+ 2471, // 516 - mg-mg
+ 2476, // 517 - mgh
+ 2479, // 518 - mgh-mz
+ 2485, // 519 - mgo
+ 2488, // 520 - mgo-cm
+ 2494, // 521 - mi
+ 2496, // 522 - mi-nz
+ 2501, // 523 - mk
+ 2503, // 524 - mk-mk
+ 2508, // 525 - ml
+ 2510, // 526 - ml-in
+ 2515, // 527 - mn
+ 2517, // 528 - mn-cyrl
+ 2524, // 529 - mn-mn
+ 2529, // 530 - mn-mong
+ 2536, // 531 - mn-mong-cn
+ 2546, // 532 - mn-mong-mn
+ 2556, // 533 - mni
+ 2559, // 534 - mni-in
+ 2565, // 535 - moh
+ 2568, // 536 - moh-ca
+ 2574, // 537 - mr
+ 2576, // 538 - mr-in
+ 2581, // 539 - ms
+ 2583, // 540 - ms-bn
+ 2588, // 541 - ms-my
+ 2593, // 542 - ms-sg
+ 2598, // 543 - mt
+ 2600, // 544 - mt-mt
+ 2605, // 545 - mua
+ 2608, // 546 - mua-cm
+ 2614, // 547 - my
+ 2616, // 548 - my-mm
+ 2621, // 549 - mzn
+ 2624, // 550 - mzn-ir
+ 2630, // 551 - naq
+ 2633, // 552 - naq-na
+ 2639, // 553 - nb
+ 2641, // 554 - nb-no
+ 2646, // 555 - nb-sj
+ 2651, // 556 - nd
+ 2653, // 557 - nd-zw
+ 2658, // 558 - nds
+ 2661, // 559 - nds-de
+ 2667, // 560 - nds-nl
+ 2673, // 561 - ne
+ 2675, // 562 - ne-in
+ 2680, // 563 - ne-np
+ 2685, // 564 - nl
+ 2687, // 565 - nl-aw
+ 2692, // 566 - nl-be
+ 2697, // 567 - nl-bq
+ 2702, // 568 - nl-cw
+ 2707, // 569 - nl-nl
+ 2712, // 570 - nl-sr
+ 2717, // 571 - nl-sx
+ 2722, // 572 - nmg
+ 2725, // 573 - nmg-cm
+ 2731, // 574 - nn
+ 2733, // 575 - nn-no
+ 2738, // 576 - nnh
+ 2741, // 577 - nnh-cm
+ 2747, // 578 - no
+ 2749, // 579 - nqo
+ 2752, // 580 - nqo-gn
+ 2758, // 581 - nr
+ 2760, // 582 - nr-za
+ 2765, // 583 - nso
+ 2768, // 584 - nso-za
+ 2774, // 585 - nus
+ 2777, // 586 - nus-ss
+ 2783, // 587 - nyn
+ 2786, // 588 - nyn-ug
+ 2792, // 589 - oc
+ 2794, // 590 - oc-fr
+ 2799, // 591 - om
+ 2801, // 592 - om-et
+ 2806, // 593 - om-ke
+ 2811, // 594 - or
+ 2813, // 595 - or-in
+ 2818, // 596 - os
+ 2820, // 597 - os-ge
+ 2825, // 598 - os-ru
+ 2830, // 599 - pa
+ 2832, // 600 - pa-arab
+ 2839, // 601 - pa-arab-pk
+ 2849, // 602 - pa-in
+ 2854, // 603 - pap
+ 2857, // 604 - pap-029
+ 2864, // 605 - pl
+ 2866, // 606 - pl-pl
+ 2871, // 607 - prg
+ 2874, // 608 - prg-001
+ 2881, // 609 - prs
+ 2884, // 610 - prs-af
+ 2890, // 611 - ps
+ 2892, // 612 - ps-af
+ 2897, // 613 - pt
+ 2899, // 614 - pt-ao
+ 2904, // 615 - pt-br
+ 2909, // 616 - pt-ch
+ 2914, // 617 - pt-cv
+ 2919, // 618 - pt-gq
+ 2924, // 619 - pt-gw
+ 2929, // 620 - pt-lu
+ 2934, // 621 - pt-mo
+ 2939, // 622 - pt-mz
+ 2944, // 623 - pt-pt
+ 2949, // 624 - pt-st
+ 2954, // 625 - pt-tl
+ 2959, // 626 - qps-latn-x-sh
+ 2972, // 627 - qps-ploc
+ 2980, // 628 - qps-ploca
+ 2989, // 629 - qps-plocm
+ 2998, // 630 - quc
+ 3001, // 631 - quc-latn
+ 3009, // 632 - quc-latn-gt
+ 3020, // 633 - quz
+ 3023, // 634 - quz-bo
+ 3029, // 635 - quz-ec
+ 3035, // 636 - quz-pe
+ 3041, // 637 - rm
+ 3043, // 638 - rm-ch
+ 3048, // 639 - rn
+ 3050, // 640 - rn-bi
+ 3055, // 641 - ro
+ 3057, // 642 - ro-md
+ 3062, // 643 - ro-ro
+ 3067, // 644 - rof
+ 3070, // 645 - rof-tz
+ 3076, // 646 - ru
+ 3078, // 647 - ru-by
+ 3083, // 648 - ru-kg
+ 3088, // 649 - ru-kz
+ 3093, // 650 - ru-md
+ 3098, // 651 - ru-ru
+ 3103, // 652 - ru-ua
+ 3108, // 653 - rw
+ 3110, // 654 - rw-rw
+ 3115, // 655 - rwk
+ 3118, // 656 - rwk-tz
+ 3124, // 657 - sa
+ 3126, // 658 - sa-in
+ 3131, // 659 - sah
+ 3134, // 660 - sah-ru
+ 3140, // 661 - saq
+ 3143, // 662 - saq-ke
+ 3149, // 663 - sbp
+ 3152, // 664 - sbp-tz
+ 3158, // 665 - sd
+ 3160, // 666 - sd-arab
+ 3167, // 667 - sd-arab-pk
+ 3177, // 668 - sd-deva
+ 3184, // 669 - sd-deva-in
+ 3194, // 670 - se
+ 3196, // 671 - se-fi
+ 3201, // 672 - se-no
+ 3206, // 673 - se-se
+ 3211, // 674 - seh
+ 3214, // 675 - seh-mz
+ 3220, // 676 - ses
+ 3223, // 677 - ses-ml
+ 3229, // 678 - sg
+ 3231, // 679 - sg-cf
+ 3236, // 680 - shi
+ 3239, // 681 - shi-latn
+ 3247, // 682 - shi-latn-ma
+ 3258, // 683 - shi-tfng
+ 3266, // 684 - shi-tfng-ma
+ 3277, // 685 - si
+ 3279, // 686 - si-lk
+ 3284, // 687 - sk
+ 3286, // 688 - sk-sk
+ 3291, // 689 - sl
+ 3293, // 690 - sl-si
+ 3298, // 691 - sma
+ 3301, // 692 - sma-no
+ 3307, // 693 - sma-se
+ 3313, // 694 - smj
+ 3316, // 695 - smj-no
+ 3322, // 696 - smj-se
+ 3328, // 697 - smn
+ 3331, // 698 - smn-fi
+ 3337, // 699 - sms
+ 3340, // 700 - sms-fi
+ 3346, // 701 - sn
+ 3348, // 702 - sn-latn
+ 3355, // 703 - sn-latn-zw
+ 3365, // 704 - so
+ 3367, // 705 - so-dj
+ 3372, // 706 - so-et
+ 3377, // 707 - so-ke
+ 3382, // 708 - so-so
+ 3387, // 709 - sq
+ 3389, // 710 - sq-al
+ 3394, // 711 - sq-mk
+ 3399, // 712 - sq-xk
+ 3404, // 713 - sr
+ 3406, // 714 - sr-cyrl
+ 3413, // 715 - sr-cyrl-ba
+ 3423, // 716 - sr-cyrl-cs
+ 3433, // 717 - sr-cyrl-me
+ 3443, // 718 - sr-cyrl-rs
+ 3453, // 719 - sr-cyrl-xk
+ 3463, // 720 - sr-latn
+ 3470, // 721 - sr-latn-ba
+ 3480, // 722 - sr-latn-cs
+ 3490, // 723 - sr-latn-me
+ 3500, // 724 - sr-latn-rs
+ 3510, // 725 - sr-latn-xk
+ 3520, // 726 - ss
+ 3522, // 727 - ss-sz
+ 3527, // 728 - ss-za
+ 3532, // 729 - ssy
+ 3535, // 730 - ssy-er
+ 3541, // 731 - st
+ 3543, // 732 - st-ls
+ 3548, // 733 - st-za
+ 3553, // 734 - sv
+ 3555, // 735 - sv-ax
+ 3560, // 736 - sv-fi
+ 3565, // 737 - sv-se
+ 3570, // 738 - sw
+ 3572, // 739 - sw-cd
+ 3577, // 740 - sw-ke
+ 3582, // 741 - sw-tz
+ 3587, // 742 - sw-ug
+ 3592, // 743 - swc
+ 3595, // 744 - swc-cd
+ 3601, // 745 - syr
+ 3604, // 746 - syr-sy
+ 3610, // 747 - ta
+ 3612, // 748 - ta-in
+ 3617, // 749 - ta-lk
+ 3622, // 750 - ta-my
+ 3627, // 751 - ta-sg
+ 3632, // 752 - te
+ 3634, // 753 - te-in
+ 3639, // 754 - teo
+ 3642, // 755 - teo-ke
+ 3648, // 756 - teo-ug
+ 3654, // 757 - tg
+ 3656, // 758 - tg-cyrl
+ 3663, // 759 - tg-cyrl-tj
+ 3673, // 760 - th
+ 3675, // 761 - th-th
+ 3680, // 762 - ti
+ 3682, // 763 - ti-er
+ 3687, // 764 - ti-et
+ 3692, // 765 - tig
+ 3695, // 766 - tig-er
+ 3701, // 767 - tk
+ 3703, // 768 - tk-tm
+ 3708, // 769 - tn
+ 3710, // 770 - tn-bw
+ 3715, // 771 - tn-za
+ 3720, // 772 - to
+ 3722, // 773 - to-to
+ 3727, // 774 - tr
+ 3729, // 775 - tr-cy
+ 3734, // 776 - tr-tr
+ 3739, // 777 - ts
+ 3741, // 778 - ts-za
+ 3746, // 779 - tt
+ 3748, // 780 - tt-ru
+ 3753, // 781 - twq
+ 3756, // 782 - twq-ne
+ 3762, // 783 - tzm
+ 3765, // 784 - tzm-arab
+ 3773, // 785 - tzm-arab-ma
+ 3784, // 786 - tzm-latn
+ 3792, // 787 - tzm-latn-dz
+ 3803, // 788 - tzm-latn-ma
+ 3814, // 789 - tzm-tfng
+ 3822, // 790 - tzm-tfng-ma
+ 3833, // 791 - ug
+ 3835, // 792 - ug-cn
+ 3840, // 793 - uk
+ 3842, // 794 - uk-ua
+ 3847, // 795 - ur
+ 3849, // 796 - ur-in
+ 3854, // 797 - ur-pk
+ 3859, // 798 - uz
+ 3861, // 799 - uz-arab
+ 3868, // 800 - uz-arab-af
+ 3878, // 801 - uz-cyrl
+ 3885, // 802 - uz-cyrl-uz
+ 3895, // 803 - uz-latn
+ 3902, // 804 - uz-latn-uz
+ 3912, // 805 - vai
+ 3915, // 806 - vai-latn
+ 3923, // 807 - vai-latn-lr
+ 3934, // 808 - vai-vaii
+ 3942, // 809 - vai-vaii-lr
+ 3953, // 810 - ve
+ 3955, // 811 - ve-za
+ 3960, // 812 - vi
+ 3962, // 813 - vi-vn
+ 3967, // 814 - vo
+ 3969, // 815 - vo-001
+ 3975, // 816 - vun
+ 3978, // 817 - vun-tz
+ 3984, // 818 - wae
+ 3987, // 819 - wae-ch
+ 3993, // 820 - wal
+ 3996, // 821 - wal-et
+ 4002, // 822 - wo
+ 4004, // 823 - wo-sn
+ 4009, // 824 - x-iv_mathan
+ 4020, // 825 - xh
+ 4022, // 826 - xh-za
+ 4027, // 827 - xog
+ 4030, // 828 - xog-ug
+ 4036, // 829 - yav
+ 4039, // 830 - yav-cm
+ 4045, // 831 - yi
+ 4047, // 832 - yi-001
+ 4053, // 833 - yo
+ 4055, // 834 - yo-bj
+ 4060, // 835 - yo-ng
+ 4065, // 836 - yue
+ 4068, // 837 - yue-hk
+ 4074, // 838 - zgh
+ 4077, // 839 - zgh-tfng
+ 4085, // 840 - zgh-tfng-ma
+ 4096, // 841 - zh
+ 4098, // 842 - zh-chs
+ 4104, // 843 - zh-cht
+ 4110, // 844 - zh-cn
+ 4115, // 845 - zh-cn_phoneb
+ 4127, // 846 - zh-cn_stroke
+ 4139, // 847 - zh-hans
+ 4146, // 848 - zh-hans-hk
+ 4156, // 849 - zh-hans-mo
+ 4166, // 850 - zh-hant
+ 4173, // 851 - zh-hk
+ 4178, // 852 - zh-hk_radstr
+ 4190, // 853 - zh-mo
+ 4195, // 854 - zh-mo_radstr
+ 4207, // 855 - zh-mo_stroke
+ 4219, // 856 - zh-sg
+ 4224, // 857 - zh-sg_phoneb
+ 4236, // 858 - zh-sg_stroke
+ 4248, // 859 - zh-tw
+ 4253, // 860 - zh-tw_pronun
+ 4265, // 861 - zh-tw_radstr
+ 4277, // 862 - zu
+ 4279, // 863 - zu-za
+ 4284
+ };
+
+ private const int NUMERIC_LOCALE_DATA_COUNT_PER_ROW = 9;
+ // s_nameIndexToNumericData is mapping from index in s_localeNamesIndices to locale data.
+ // each row in the table will have the following data:
+ // Lcid, Ansi codepage, Oem codepage, MAC codepage, EBCDIC codepage, Geo Id, Digit Substitution, specific locale index, Console locale index
+ private static readonly int[] s_nameIndexToNumericData = new int[]
+ {
+ // Lcid, Ansi CP, Oem CP, MAC CP, EBCDIC CP, Geo Id, digit substitution, Specific culture index, keyboard Id, Console locale index // index - locale name
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 3 , 240 , // 0 - aa
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3e , 1 , 1 , 240 , // 1 - aa-dj
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 2 , 240 , // 2 - aa-er
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 3 , 240 , // 3 - aa-et
+ 0x36 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 6 , 6 , // 4 - af
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xfe , 1 , 5 , 240 , // 5 - af-na
+ 0x436 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 6 , 6 , // 6 - af-za
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 8 , 240 , // 7 - agq
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 8 , 240 , // 8 - agq-cm
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x59 , 1 , 10 , 240 , // 9 - ak
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x59 , 1 , 10 , 240 , // 10 - ak-gh
+ 0x5e , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 12 , 143 , // 11 - am
+ 0x45e , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 12 , 143 , // 12 - am-et
+ 0x1 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xcd , 0 , 33 , 143 , // 13 - ar
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x989e, 0 , 14 , 240 , // 14 - ar-001
+ 0x3801 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xe0 , 0 , 15 , 143 , // 15 - ar-ae
+ 0x3c01 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x11 , 0 , 16 , 143 , // 16 - ar-bh
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x3e , 0 , 17 , 240 , // 17 - ar-dj
+ 0x1401 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x4 , 1 , 18 , 300 , // 18 - ar-dz
+ 0xc01 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x43 , 0 , 19 , 143 , // 19 - ar-eg
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x47 , 0 , 20 , 240 , // 20 - ar-er
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x75 , 0 , 21 , 240 , // 21 - ar-il
+ 0x801 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x79 , 0 , 22 , 143 , // 22 - ar-iq
+ 0x2c01 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x7e , 0 , 23 , 143 , // 23 - ar-jo
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x32 , 0 , 24 , 240 , // 24 - ar-km
+ 0x3401 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x88 , 0 , 25 , 143 , // 25 - ar-kw
+ 0x3001 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x8b , 0 , 26 , 143 , // 26 - ar-lb
+ 0x1001 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x94 , 1 , 27 , 143 , // 27 - ar-ly
+ 0x1801 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x9f , 1 , 28 , 300 , // 28 - ar-ma
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xa2 , 0 , 29 , 240 , // 29 - ar-mr
+ 0x2001 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xa4 , 0 , 30 , 143 , // 30 - ar-om
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xb8 , 0 , 31 , 240 , // 31 - ar-ps
+ 0x4001 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xc5 , 0 , 32 , 143 , // 32 - ar-qa
+ 0x401 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xcd , 0 , 33 , 143 , // 33 - ar-sa
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xdb , 0 , 34 , 240 , // 34 - ar-sd
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xd8 , 0 , 35 , 240 , // 35 - ar-so
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x114 , 0 , 36 , 240 , // 36 - ar-ss
+ 0x2801 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xde , 0 , 37 , 143 , // 37 - ar-sy
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x29 , 0 , 38 , 240 , // 38 - ar-td
+ 0x1c01 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xea , 1 , 39 , 300 , // 39 - ar-tn
+ 0x2401 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x105 , 0 , 40 , 143 , // 40 - ar-ye
+ 0x7a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x2e , 1 , 42 , 42 , // 41 - arn
+ 0x47a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x2e , 1 , 42 , 42 , // 42 - arn-cl
+ 0x4d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 44 , 143 , // 43 - as
+ 0x44d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 44 , 143 , // 44 - as-in
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 46 , 240 , // 45 - asa
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 46 , 240 , // 46 - asa-tz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd9 , 1 , 48 , 240 , // 47 - ast
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd9 , 1 , 48 , 240 , // 48 - ast-es
+ 0x2c , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0x5 , 1 , 53 , 53 , // 49 - az
+ 0x742c , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x5 , 1 , 51 , 51 , // 50 - az-cyrl
+ 0x82c , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x5 , 1 , 51 , 51 , // 51 - az-cyrl-az
+ 0x782c , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0x5 , 1 , 53 , 53 , // 52 - az-latn
+ 0x42c , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0x5 , 1 , 53 , 53 , // 53 - az-latn-az
+ 0x6d , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 55 , 55 , // 54 - ba
+ 0x46d , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 55 , 55 , // 55 - ba-ru
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 57 , 240 , // 56 - bas
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 57 , 240 , // 57 - bas-cm
+ 0x23 , 0x4e3 , 0x362 , 0x2717, 0x1f4 , 0x1d , 1 , 59 , 59 , // 58 - be
+ 0x423 , 0x4e3 , 0x362 , 0x2717, 0x1f4 , 0x1d , 1 , 59 , 59 , // 59 - be-by
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x107 , 1 , 61 , 240 , // 60 - bem
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x107 , 1 , 61 , 240 , // 61 - bem-zm
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 63 , 240 , // 62 - bez
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 63 , 240 , // 63 - bez-tz
+ 0x2 , 0x4e3 , 0x362 , 0x2717, 0x5221, 0x23 , 1 , 65 , 65 , // 64 - bg
+ 0x402 , 0x4e3 , 0x362 , 0x2717, 0x5221, 0x23 , 1 , 65 , 65 , // 65 - bg-bg
+ 0x66 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 67 , 240 , // 66 - bin
+ 0x466 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 67 , 240 , // 67 - bin-ng
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 70 , 240 , // 68 - bm
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 70 , 240 , // 69 - bm-latn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 70 , 240 , // 70 - bm-latn-ml
+ 0x45 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x17 , 1 , 72 , 143 , // 71 - bn
+ 0x845 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x17 , 1 , 72 , 143 , // 72 - bn-bd
+ 0x445 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 73 , 143 , // 73 - bn-in
+ 0x51 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 75 , 143 , // 74 - bo
+ 0x451 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 75 , 143 , // 75 - bo-cn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 76 , 240 , // 76 - bo-in
+ 0x7e , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 78 , 78 , // 77 - br
+ 0x47e , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 78 , 78 , // 78 - br-fr
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 80 , 240 , // 79 - brx
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 80 , 240 , // 80 - brx-in
+ 0x781a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 85 , 85 , // 81 - bs
+ 0x641a , 0x4e3 , 0x357 , 0x2762, 0x366 , 0x19 , 1 , 83 , 83 , // 82 - bs-cyrl
+ 0x201a , 0x4e3 , 0x357 , 0x2762, 0x366 , 0x19 , 1 , 83 , 83 , // 83 - bs-cyrl-ba
+ 0x681a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 85 , 85 , // 84 - bs-latn
+ 0x141a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 85 , 85 , // 85 - bs-latn-ba
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 87 , 240 , // 86 - byn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 87 , 240 , // 87 - byn-er
+ 0x3 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 90 , 90 , // 88 - ca
+ 0x1000 , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0x8 , 1 , 89 , 240 , // 89 - ca-ad
+ 0x403 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 90 , 90 , // 90 - ca-es
+ 0x803 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 91 , 90 , // 91 - ca-es-valencia
+ 0x1000 , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0x54 , 1 , 92 , 240 , // 92 - ca-fr
+ 0x1000 , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0x76 , 1 , 93 , 240 , // 93 - ca-it
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 95 , 240 , // 94 - ce
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 95 , 240 , // 95 - ce-ru
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 97 , 240 , // 96 - cgg
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 97 , 240 , // 97 - cgg-ug
+ 0x5c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 100 , 240 , // 98 - chr
+ 0x7c5c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 100 , 240 , // 99 - chr-cher
+ 0x45c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 100 , 240 , // 100 - chr-cher-us
+ 0x83 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 102 , 102 , // 101 - co
+ 0x483 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 102 , 102 , // 102 - co-fr
+ 0x5 , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x4b , 1 , 104 , 104 , // 103 - cs
+ 0x405 , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x4b , 1 , 104 , 104 , // 104 - cs-cz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 106 , 240 , // 105 - cu
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 106 , 240 , // 106 - cu-ru
+ 0x52 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 108 , 108 , // 107 - cy
+ 0x452 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 108 , 108 , // 108 - cy-gb
+ 0x6 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0x3d , 1 , 110 , 110 , // 109 - da
+ 0x406 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0x3d , 1 , 110 , 110 , // 110 - da-dk
+ 0x1000 , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0x5d , 1 , 111 , 240 , // 111 - da-gl
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 113 , 240 , // 112 - dav
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 113 , 240 , // 113 - dav-ke
+ 0x7 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x5e , 1 , 118 , 118 , // 114 - de
+ 0xc07 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0xe , 1 , 115 , 115 , // 115 - de-at
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x15 , 1 , 116 , 240 , // 116 - de-be
+ 0x807 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0xdf , 1 , 117 , 117 , // 117 - de-ch
+ 0x407 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x5e , 1 , 118 , 118 , // 118 - de-de
+ 0x10407, 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x5e , 1 , 118 , 118 , // 119 - de-de_phoneb
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x76 , 1 , 120 , 240 , // 120 - de-it
+ 0x1407 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x91 , 1 , 121 , 121 , // 121 - de-li
+ 0x1007 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0x93 , 1 , 122 , 122 , // 122 - de-lu
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xad , 1 , 124 , 240 , // 123 - dje
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xad , 1 , 124 , 240 , // 124 - dje-ne
+ 0x7c2e , 0x4e4 , 0x352 , 0x2710, 0x366 , 0x5e , 1 , 126 , 126 , // 125 - dsb
+ 0x82e , 0x4e4 , 0x352 , 0x2710, 0x366 , 0x5e , 1 , 126 , 126 , // 126 - dsb-de
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 128 , 240 , // 127 - dua
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 128 , 240 , // 128 - dua-cm
+ 0x65 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa5 , 1 , 130 , 143 , // 129 - dv
+ 0x465 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa5 , 1 , 130 , 143 , // 130 - dv-mv
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd2 , 1 , 132 , 240 , // 131 - dyo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd2 , 1 , 132 , 240 , // 132 - dyo-sn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x22 , 2 , 134 , 240 , // 133 - dz
+ 0xc51 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x22 , 2 , 134 , 240 , // 134 - dz-bt
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 136 , 240 , // 135 - ebu
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 136 , 240 , // 136 - ebu-ke
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x59 , 1 , 138 , 240 , // 137 - ee
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x59 , 1 , 138 , 240 , // 138 - ee-gh
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xe8 , 1 , 139 , 240 , // 139 - ee-tg
+ 0x8 , 0x4e5 , 0x2e1 , 0x2716, 0x4f31, 0x62 , 1 , 142 , 142 , // 140 - el
+ 0x1000 , 0x4e5 , 0x2e1 , 0x2716, 0x4f31, 0x3b , 1 , 141 , 240 , // 141 - el-cy
+ 0x408 , 0x4e5 , 0x2e1 , 0x2716, 0x4f31, 0x62 , 1 , 142 , 142 , // 142 - el-gr
+ 0x9 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , 240 , 240 , // 143 - en
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x989e, 1 , 144 , 240 , // 144 - en-001
+ 0x2409 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x993248, 1 , 145 , 145 , // 145 - en-029
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x292d, 1 , 146 , 240 , // 146 - en-150
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x2 , 1 , 147 , 240 , // 147 - en-ag
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x12c , 1 , 148 , 240 , // 148 - en-ai
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa , 1 , 149 , 240 , // 149 - en-as
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xe , 1 , 150 , 240 , // 150 - en-at
+ 0xc09 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc , 1 , 151 , 151 , // 151 - en-au
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x12 , 1 , 152 , 240 , // 152 - en-bb
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15 , 1 , 153 , 240 , // 153 - en-be
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x26 , 1 , 154 , 240 , // 154 - en-bi
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x14 , 1 , 155 , 240 , // 155 - en-bm
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x16 , 1 , 156 , 240 , // 156 - en-bs
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x13 , 1 , 157 , 240 , // 157 - en-bw
+ 0x2809 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x18 , 1 , 158 , 158 , // 158 - en-bz
+ 0x1009 , 0x4e4 , 0x352 , 0x2710, 0x25 , 0x27 , 1 , 159 , 159 , // 159 - en-ca
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x137 , 1 , 160 , 240 , // 160 - en-cc
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdf , 1 , 161 , 240 , // 161 - en-ch
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x138 , 1 , 162 , 240 , // 162 - en-ck
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x31 , 1 , 163 , 240 , // 163 - en-cm
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x135 , 1 , 164 , 240 , // 164 - en-cx
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3b , 1 , 165 , 240 , // 165 - en-cy
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 166 , 240 , // 166 - en-de
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3d , 1 , 167 , 240 , // 167 - en-dk
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x3f , 1 , 168 , 240 , // 168 - en-dm
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x47 , 1 , 169 , 240 , // 169 - en-er
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x4d , 1 , 170 , 240 , // 170 - en-fi
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x4e , 1 , 171 , 240 , // 171 - en-fj
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x13b , 1 , 172 , 240 , // 172 - en-fk
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x50 , 1 , 173 , 240 , // 173 - en-fm
+ 0x809 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 174 , 174 , // 174 - en-gb
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x5b , 1 , 175 , 240 , // 175 - en-gd
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x144 , 1 , 176 , 240 , // 176 - en-gg
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x59 , 1 , 177 , 240 , // 177 - en-gh
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x5a , 1 , 178 , 240 , // 178 - en-gi
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x56 , 1 , 179 , 240 , // 179 - en-gm
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x142 , 1 , 180 , 240 , // 180 - en-gu
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x65 , 1 , 181 , 240 , // 181 - en-gy
+ 0x3c09 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x68 , 1 , 182 , 240 , // 182 - en-hk
+ 0x3809 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 183 , 240 , // 183 - en-id
+ 0x1809 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x44 , 1 , 184 , 184 , // 184 - en-ie
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x75 , 1 , 185 , 240 , // 185 - en-il
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x3b16, 1 , 186 , 240 , // 186 - en-im
+ 0x4009 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x71 , 1 , 187 , 187 , // 187 - en-in
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x72 , 1 , 188 , 240 , // 188 - en-io
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x148 , 1 , 189 , 240 , // 189 - en-je
+ 0x2009 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x7c , 1 , 190 , 190 , // 190 - en-jm
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x81 , 1 , 191 , 240 , // 191 - en-ke
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x85 , 1 , 192 , 240 , // 192 - en-ki
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xcf , 1 , 193 , 240 , // 193 - en-kn
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x133 , 1 , 194 , 240 , // 194 - en-ky
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xda , 1 , 195 , 240 , // 195 - en-lc
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x8e , 1 , 196 , 240 , // 196 - en-lr
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x92 , 1 , 197 , 240 , // 197 - en-ls
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x95 , 1 , 198 , 240 , // 198 - en-mg
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc7 , 1 , 199 , 240 , // 199 - en-mh
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x97 , 1 , 200 , 240 , // 200 - en-mo
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x151 , 1 , 201 , 240 , // 201 - en-mp
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x14c , 1 , 202 , 240 , // 202 - en-ms
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa3 , 1 , 203 , 240 , // 203 - en-mt
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa0 , 1 , 204 , 240 , // 204 - en-mu
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x9c , 1 , 205 , 240 , // 205 - en-mw
+ 0x4409 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xa7 , 1 , 206 , 206 , // 206 - en-my
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xfe , 1 , 207 , 240 , // 207 - en-na
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x150 , 1 , 208 , 240 , // 208 - en-nf
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 209 , 240 , // 209 - en-ng
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb0 , 1 , 210 , 240 , // 210 - en-nl
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb4 , 1 , 211 , 240 , // 211 - en-nr
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x14f , 1 , 212 , 240 , // 212 - en-nu
+ 0x1409 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb7 , 1 , 213 , 213 , // 213 - en-nz
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc2 , 1 , 214 , 240 , // 214 - en-pg
+ 0x3409 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xc9 , 1 , 215 , 215 , // 215 - en-ph
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xbe , 1 , 216 , 240 , // 216 - en-pk
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x153 , 1 , 217 , 240 , // 217 - en-pn
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xca , 1 , 218 , 240 , // 218 - en-pr
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc3 , 1 , 219 , 240 , // 219 - en-pw
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xcc , 1 , 220 , 240 , // 220 - en-rw
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x1e , 1 , 221 , 240 , // 221 - en-sb
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd0 , 1 , 222 , 240 , // 222 - en-sc
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xdb , 1 , 223 , 240 , // 223 - en-sd
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdd , 1 , 224 , 240 , // 224 - en-se
+ 0x4809 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xd7 , 1 , 225 , 225 , // 225 - en-sg
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x157 , 1 , 226 , 240 , // 226 - en-sh
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd4 , 1 , 227 , 240 , // 227 - en-si
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd5 , 1 , 228 , 240 , // 228 - en-sl
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x114 , 1 , 229 , 240 , // 229 - en-ss
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x78f7, 1 , 230 , 240 , // 230 - en-sx
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x104 , 1 , 231 , 240 , // 231 - en-sz
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15d , 1 , 232 , 240 , // 232 - en-tc
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15b , 1 , 233 , 240 , // 233 - en-tk
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xe7 , 1 , 234 , 240 , // 234 - en-to
+ 0x2c09 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xe1 , 1 , 235 , 235 , // 235 - en-tt
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xec , 1 , 236 , 240 , // 236 - en-tv
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xef , 1 , 237 , 240 , // 237 - en-tz
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xf0 , 1 , 238 , 240 , // 238 - en-ug
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x9a55d40, 1 , 239 , 240 , // 239 - en-um
+ 0x409 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , 240 , 240 , // 240 - en-us
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xf8 , 1 , 241 , 240 , // 241 - en-vc
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15f , 1 , 242 , 240 , // 242 - en-vg
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xfc , 1 , 243 , 240 , // 243 - en-vi
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xae , 1 , 244 , 240 , // 244 - en-vu
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x103 , 1 , 245 , 240 , // 245 - en-ws
+ 0x1c09 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xd1 , 1 , 246 , 246 , // 246 - en-za
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x107 , 1 , 247 , 240 , // 247 - en-zm
+ 0x3009 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x108 , 1 , 248 , 248 , // 248 - en-zw
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 250 , 240 , // 249 - eo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 250 , 240 , // 250 - eo-001
+ 0xa , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xd9 , 1 , 262 , 262 , // 251 - es
+ 0x580a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x9a55d41, 1 , 252 , 240 , // 252 - es-419
+ 0x2c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb , 1 , 253 , 253 , // 253 - es-ar
+ 0x400a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x1a , 1 , 254 , 254 , // 254 - es-bo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x20 , 1 , 255 , 240 , // 255 - es-br
+ 0x340a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x2e , 1 , 256 , 256 , // 256 - es-cl
+ 0x240a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x33 , 1 , 257 , 257 , // 257 - es-co
+ 0x140a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x36 , 1 , 258 , 258 , // 258 - es-cr
+ 0x5c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x38 , 1 , 259 , 240 , // 259 - es-cu
+ 0x1c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x41 , 1 , 260 , 260 , // 260 - es-do
+ 0x300a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x42 , 1 , 261 , 261 , // 261 - es-ec
+ 0xc0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xd9 , 1 , 262 , 262 , // 262 - es-es
+ 0x40a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xd9 , 1 , 263 , 263 , // 263 - es-es_tradnl
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x45 , 1 , 264 , 240 , // 264 - es-gq
+ 0x100a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x63 , 1 , 265 , 265 , // 265 - es-gt
+ 0x480a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x6a , 1 , 266 , 266 , // 266 - es-hn
+ 0x80a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xa6 , 1 , 267 , 267 , // 267 - es-mx
+ 0x4c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb6 , 1 , 268 , 268 , // 268 - es-ni
+ 0x180a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xc0 , 1 , 269 , 269 , // 269 - es-pa
+ 0x280a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xbb , 1 , 270 , 270 , // 270 - es-pe
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xc9 , 1 , 271 , 240 , // 271 - es-ph
+ 0x500a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xca , 1 , 272 , 272 , // 272 - es-pr
+ 0x3c0a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb9 , 1 , 273 , 273 , // 273 - es-py
+ 0x440a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x48 , 1 , 274 , 274 , // 274 - es-sv
+ 0x540a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xf4 , 1 , 275 , 275 , // 275 - es-us
+ 0x380a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xf6 , 1 , 276 , 276 , // 276 - es-uy
+ 0x200a , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xf9 , 1 , 277 , 277 , // 277 - es-ve
+ 0x25 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x46 , 1 , 279 , 279 , // 278 - et
+ 0x425 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x46 , 1 , 279 , 279 , // 279 - et-ee
+ 0x2d , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0xd9 , 1 , 281 , 240 , // 280 - eu
+ 0x42d , 0x4e4 , 0x352 , 0x2 , 0x1f4 , 0xd9 , 1 , 281 , 240 , // 281 - eu-es
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 283 , 240 , // 282 - ewo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 283 , 240 , // 283 - ewo-cm
+ 0x29 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x74 , 0 , 285 , 143 , // 284 - fa
+ 0x429 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x74 , 0 , 285 , 143 , // 285 - fa-ir
+ 0x67 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 290 , 290 , // 286 - ff
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x31 , 1 , 287 , 240 , // 287 - ff-cm
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x64 , 1 , 288 , 240 , // 288 - ff-gn
+ 0x7c67 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 290 , 290 , // 289 - ff-latn
+ 0x867 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 290 , 290 , // 290 - ff-latn-sn
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xa2 , 1 , 291 , 240 , // 291 - ff-mr
+ 0x467 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xaf , 1 , 292 , 240 , // 292 - ff-ng
+ 0xb , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 294 , 294 , // 293 - fi
+ 0x40b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 294 , 294 , // 294 - fi-fi
+ 0x64 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xc9 , 1 , 296 , 296 , // 295 - fil
+ 0x464 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xc9 , 1 , 296 , 296 , // 296 - fil-ph
+ 0x38 , 0x4e4 , 0x352 , 0x275f, 0x4f35, 0x51 , 1 , 299 , 299 , // 297 - fo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3d , 1 , 298 , 240 , // 298 - fo-dk
+ 0x438 , 0x4e4 , 0x352 , 0x275f, 0x4f35, 0x51 , 1 , 299 , 299 , // 299 - fo-fo
+ 0xc , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 316 , 316 , // 300 - fr
+ 0x1c0c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x993248, 1 , 301 , 316 , // 301 - fr-029
+ 0x80c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x15 , 1 , 302 , 302 , // 302 - fr-be
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xf5 , 1 , 303 , 240 , // 303 - fr-bf
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x26 , 1 , 304 , 240 , // 304 - fr-bi
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x1c , 1 , 305 , 240 , // 305 - fr-bj
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x9a55c4f, 1 , 306 , 240 , // 306 - fr-bl
+ 0xc0c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x27 , 1 , 307 , 307 , // 307 - fr-ca
+ 0x240c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x2c , 1 , 308 , 240 , // 308 - fr-cd
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x37 , 1 , 309 , 240 , // 309 - fr-cf
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x2b , 1 , 310 , 240 , // 310 - fr-cg
+ 0x100c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xdf , 1 , 311 , 311 , // 311 - fr-ch
+ 0x300c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x77 , 1 , 312 , 240 , // 312 - fr-ci
+ 0x2c0c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x31 , 1 , 313 , 240 , // 313 - fr-cm
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x3e , 1 , 314 , 240 , // 314 - fr-dj
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x4 , 1 , 315 , 240 , // 315 - fr-dz
+ 0x40c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 316 , 316 , // 316 - fr-fr
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x57 , 1 , 317 , 240 , // 317 - fr-ga
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x13d , 1 , 318 , 240 , // 318 - fr-gf
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x64 , 1 , 319 , 240 , // 319 - fr-gn
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x141 , 1 , 320 , 240 , // 320 - fr-gp
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x45 , 1 , 321 , 240 , // 321 - fr-gq
+ 0x3c0c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x67 , 1 , 322 , 240 , // 322 - fr-ht
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x32 , 1 , 323 , 240 , // 323 - fr-km
+ 0x140c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x93 , 1 , 324 , 324 , // 324 - fr-lu
+ 0x380c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x9f , 1 , 325 , 240 , // 325 - fr-ma
+ 0x180c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x9e , 1 , 326 , 326 , // 326 - fr-mc
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x7bda, 1 , 327 , 240 , // 327 - fr-mf
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x95 , 1 , 328 , 240 , // 328 - fr-mg
+ 0x340c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x9d , 1 , 329 , 240 , // 329 - fr-ml
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x14a , 1 , 330 , 240 , // 330 - fr-mq
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xa2 , 1 , 331 , 240 , // 331 - fr-mr
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xa0 , 1 , 332 , 240 , // 332 - fr-mu
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x14e , 1 , 333 , 240 , // 333 - fr-nc
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xad , 1 , 334 , 240 , // 334 - fr-ne
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x13e , 1 , 335 , 240 , // 335 - fr-pf
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xce , 1 , 336 , 240 , // 336 - fr-pm
+ 0x200c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xc6 , 1 , 337 , 240 , // 337 - fr-re
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xcc , 1 , 338 , 240 , // 338 - fr-rw
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd0 , 1 , 339 , 240 , // 339 - fr-sc
+ 0x280c , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 340 , 240 , // 340 - fr-sn
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xde , 1 , 341 , 240 , // 341 - fr-sy
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x29 , 1 , 342 , 240 , // 342 - fr-td
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xe8 , 1 , 343 , 240 , // 343 - fr-tg
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xea , 1 , 344 , 240 , // 344 - fr-tn
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xae , 1 , 345 , 240 , // 345 - fr-vu
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x160 , 1 , 346 , 240 , // 346 - fr-wf
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x14b , 1 , 347 , 240 , // 347 - fr-yt
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x76 , 1 , 349 , 240 , // 348 - fur
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x76 , 1 , 349 , 240 , // 349 - fur-it
+ 0x62 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb0 , 1 , 351 , 351 , // 350 - fy
+ 0x462 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb0 , 1 , 351 , 351 , // 351 - fy-nl
+ 0x3c , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x44 , 1 , 353 , 353 , // 352 - ga
+ 0x83c , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x44 , 1 , 353 , 353 , // 353 - ga-ie
+ 0x91 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 355 , 355 , // 354 - gd
+ 0x491 , 0x4e4 , 0x352 , 0x2710, 0x4f3d, 0xf2 , 1 , 355 , 355 , // 355 - gd-gb
+ 0x56 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 357 , 357 , // 356 - gl
+ 0x456 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd9 , 1 , 357 , 357 , // 357 - gl-es
+ 0x74 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb9 , 1 , 359 , 359 , // 358 - gn
+ 0x474 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xb9 , 1 , 359 , 359 , // 359 - gn-py
+ 0x84 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xdf , 1 , 361 , 240 , // 360 - gsw
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xdf , 1 , 361 , 240 , // 361 - gsw-ch
+ 0x484 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 362 , 362 , // 362 - gsw-fr
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x91 , 1 , 363 , 240 , // 363 - gsw-li
+ 0x47 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 365 , 143 , // 364 - gu
+ 0x447 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 365 , 143 , // 365 - gu-in
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 367 , 240 , // 366 - guz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 367 , 240 , // 367 - guz-ke
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3b16, 1 , 369 , 240 , // 368 - gv
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3b16, 1 , 369 , 240 , // 369 - gv-im
+ 0x68 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 374 , 374 , // 370 - ha
+ 0x7c68 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 374 , 374 , // 371 - ha-latn
+ 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x59 , 1 , 372 , 240 , // 372 - ha-latn-gh
+ 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xad , 1 , 373 , 240 , // 373 - ha-latn-ne
+ 0x468 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 374 , 374 , // 374 - ha-latn-ng
+ 0x75 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , 376 , 376 , // 375 - haw
+ 0x475 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , 376 , 376 , // 376 - haw-us
+ 0xd , 0x4e7 , 0x35e , 0x2715, 0x1f4 , 0x75 , 1 , 378 , 143 , // 377 - he
+ 0x40d , 0x4e7 , 0x35e , 0x2715, 0x1f4 , 0x75 , 1 , 378 , 143 , // 378 - he-il
+ 0x39 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 380 , 143 , // 379 - hi
+ 0x439 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 380 , 143 , // 380 - hi-in
+ 0x1a , 0x4e2 , 0x354 , 0x2762, 0x1f4 , 0x6c , 1 , 383 , 383 , // 381 - hr
+ 0x101a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 382 , 382 , // 382 - hr-ba
+ 0x41a , 0x4e2 , 0x354 , 0x2762, 0x1f4 , 0x6c , 1 , 383 , 383 , // 383 - hr-hr
+ 0x2e , 0x4e4 , 0x352 , 0x2710, 0x366 , 0x5e , 1 , 385 , 385 , // 384 - hsb
+ 0x42e , 0x4e4 , 0x352 , 0x2710, 0x366 , 0x5e , 1 , 385 , 385 , // 385 - hsb-de
+ 0xe , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x6d , 1 , 387 , 387 , // 386 - hu
+ 0x40e , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x6d , 1 , 387 , 387 , // 387 - hu-hu
+ 0x1040e, 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x6d , 1 , 387 , 387 , // 388 - hu-hu_technl
+ 0x2b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x7 , 1 , 390 , 390 , // 389 - hy
+ 0x42b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x7 , 1 , 390 , 390 , // 390 - hy-am
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x54 , 1 , 393 , 240 , // 391 - ia
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 392 , 240 , // 392 - ia-001
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x54 , 1 , 393 , 240 , // 393 - ia-fr
+ 0x69 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 395 , 240 , // 394 - ibb
+ 0x469 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 395 , 240 , // 395 - ibb-ng
+ 0x21 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 397 , 397 , // 396 - id
+ 0x421 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 397 , 397 , // 397 - id-id
+ 0x70 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 399 , 399 , // 398 - ig
+ 0x470 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 399 , 399 , // 399 - ig-ng
+ 0x78 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 401 , 143 , // 400 - ii
+ 0x478 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 401 , 143 , // 401 - ii-cn
+ 0xf , 0x4e4 , 0x352 , 0x275f, 0x5187, 0x6e , 1 , 403 , 403 , // 402 - is
+ 0x40f , 0x4e4 , 0x352 , 0x275f, 0x5187, 0x6e , 1 , 403 , 403 , // 403 - is-is
+ 0x10 , 0x4e4 , 0x352 , 0x2710, 0x4f38, 0x76 , 1 , 406 , 406 , // 404 - it
+ 0x810 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xdf , 1 , 405 , 405 , // 405 - it-ch
+ 0x410 , 0x4e4 , 0x352 , 0x2710, 0x4f38, 0x76 , 1 , 406 , 406 , // 406 - it-it
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f38, 0xd6 , 1 , 407 , 240 , // 407 - it-sm
+ 0x5d , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x27 , 1 , 412 , 412 , // 408 - iu
+ 0x785d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x27 , 1 , 410 , 143 , // 409 - iu-cans
+ 0x45d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x27 , 1 , 410 , 143 , // 410 - iu-cans-ca
+ 0x7c5d , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x27 , 1 , 412 , 412 , // 411 - iu-latn
+ 0x85d , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x27 , 1 , 412 , 412 , // 412 - iu-latn-ca
+ 0x11 , 0x3a4 , 0x3a4 , 0x2711, 0x4f42, 0x7a , 1 , 414 , 414 , // 413 - ja
+ 0x411 , 0x3a4 , 0x3a4 , 0x2711, 0x4f42, 0x7a , 1 , 414 , 414 , // 414 - ja-jp
+ 0x40411, 0x3a4 , 0x3a4 , 0x2711, 0x4f42, 0x7a , 1 , 414 , 414 , // 415 - ja-jp_radstr
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 417 , 240 , // 416 - jgo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 417 , 240 , // 417 - jgo-cm
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 419 , 240 , // 418 - jmc
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 419 , 240 , // 419 - jmc-tz
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 424 , 424 , // 420 - jv
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 422 , 424 , // 421 - jv-java
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 422 , 424 , // 422 - jv-java-id
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 424 , 424 , // 423 - jv-latn
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f , 1 , 424 , 424 , // 424 - jv-latn-id
+ 0x37 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 426 , 426 , // 425 - ka
+ 0x437 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 426 , 426 , // 426 - ka-ge
+ 0x10437, 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 426 , 426 , // 427 - ka-ge_modern
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x4 , 1 , 429 , 240 , // 428 - kab
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x4 , 1 , 429 , 240 , // 429 - kab-dz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 431 , 240 , // 430 - kam
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 431 , 240 , // 431 - kam-ke
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 433 , 240 , // 432 - kde
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 433 , 240 , // 433 - kde-tz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x39 , 1 , 435 , 240 , // 434 - kea
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x39 , 1 , 435 , 240 , // 435 - kea-cv
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 437 , 240 , // 436 - khq
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 437 , 240 , // 437 - khq-ml
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 439 , 240 , // 438 - ki
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 439 , 240 , // 439 - ki-ke
+ 0x3f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x89 , 1 , 441 , 441 , // 440 - kk
+ 0x43f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x89 , 1 , 441 , 441 , // 441 - kk-kz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 443 , 240 , // 442 - kkj
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 443 , 240 , // 443 - kkj-cm
+ 0x6f , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0x5d , 1 , 445 , 445 , // 444 - kl
+ 0x46f , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0x5d , 1 , 445 , 445 , // 445 - kl-gl
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 447 , 240 , // 446 - kln
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 447 , 240 , // 447 - kln-ke
+ 0x53 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x28 , 2 , 449 , 143 , // 448 - km
+ 0x453 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x28 , 2 , 449 , 143 , // 449 - km-kh
+ 0x4b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 451 , 143 , // 450 - kn
+ 0x44b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 451 , 143 , // 451 - kn-in
+ 0x12 , 0x3b5 , 0x3b5 , 0x2713, 0x5161, 0x86 , 1 , 454 , 454 , // 452 - ko
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x83 , 1 , 453 , 240 , // 453 - ko-kp
+ 0x412 , 0x3b5 , 0x3b5 , 0x2713, 0x5161, 0x86 , 1 , 454 , 454 , // 454 - ko-kr
+ 0x57 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 456 , 143 , // 455 - kok
+ 0x457 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 456 , 143 , // 456 - kok-in
+ 0x71 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 458 , 240 , // 457 - kr
+ 0x471 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xaf , 1 , 458 , 240 , // 458 - kr-ng
+ 0x60 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 2 , 461 , 240 , // 459 - ks
+ 0x460 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 2 , 461 , 240 , // 460 - ks-arab
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 2 , 461 , 240 , // 461 - ks-arab-in
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 463 , 187 , // 462 - ks-deva
+ 0x860 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 463 , 187 , // 463 - ks-deva-in
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 465 , 240 , // 464 - ksb
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 465 , 240 , // 465 - ksb-tz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 467 , 240 , // 466 - ksf
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 467 , 240 , // 467 - ksf-cm
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 469 , 240 , // 468 - ksh
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 469 , 240 , // 469 - ksh-de
+ 0x92 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x79 , 0 , 472 , 143 , // 470 - ku
+ 0x7c92 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x79 , 0 , 472 , 143 , // 471 - ku-arab
+ 0x492 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x79 , 0 , 472 , 143 , // 472 - ku-arab-iq
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 0 , 473 , 240 , // 473 - ku-arab-ir
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf2 , 1 , 475 , 240 , // 474 - kw
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf2 , 1 , 475 , 240 , // 475 - kw-gb
+ 0x40 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x82 , 1 , 477 , 477 , // 476 - ky
+ 0x440 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x82 , 1 , 477 , 477 , // 477 - ky-kg
+ 0x76 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x989e, 1 , 479 , 143 , // 478 - la
+ 0x476 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0x989e, 1 , 479 , 143 , // 479 - la-001
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 481 , 240 , // 480 - lag
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 481 , 240 , // 481 - lag-tz
+ 0x6e , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x93 , 1 , 483 , 483 , // 482 - lb
+ 0x46e , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x93 , 1 , 483 , 483 , // 483 - lb-lu
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 485 , 240 , // 484 - lg
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 485 , 240 , // 485 - lg-ug
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 487 , 240 , // 486 - lkt
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf4 , 1 , 487 , 240 , // 487 - lkt-us
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2c , 1 , 490 , 240 , // 488 - ln
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9 , 1 , 489 , 240 , // 489 - ln-ao
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2c , 1 , 490 , 240 , // 490 - ln-cd
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x37 , 1 , 491 , 240 , // 491 - ln-cf
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2b , 1 , 492 , 240 , // 492 - ln-cg
+ 0x54 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8a , 1 , 494 , 143 , // 493 - lo
+ 0x454 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8a , 1 , 494 , 143 , // 494 - lo-la
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 2 , 497 , 240 , // 495 - lrc
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x79 , 2 , 496 , 240 , // 496 - lrc-iq
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 2 , 497 , 240 , // 497 - lrc-ir
+ 0x27 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x8d , 1 , 499 , 499 , // 498 - lt
+ 0x427 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x8d , 1 , 499 , 499 , // 499 - lt-lt
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2c , 1 , 501 , 240 , // 500 - lu
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2c , 1 , 501 , 240 , // 501 - lu-cd
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 503 , 240 , // 502 - luo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 503 , 240 , // 503 - luo-ke
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 505 , 240 , // 504 - luy
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 505 , 240 , // 505 - luy-ke
+ 0x26 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x8c , 1 , 507 , 507 , // 506 - lv
+ 0x426 , 0x4e9 , 0x307 , 0x272d, 0x1f4 , 0x8c , 1 , 507 , 507 , // 507 - lv-lv
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 509 , 240 , // 508 - mas
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 509 , 240 , // 509 - mas-ke
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 510 , 240 , // 510 - mas-tz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 512 , 240 , // 511 - mer
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 512 , 240 , // 512 - mer-ke
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa0 , 1 , 514 , 240 , // 513 - mfe
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa0 , 1 , 514 , 240 , // 514 - mfe-mu
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x95 , 1 , 516 , 240 , // 515 - mg
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x95 , 1 , 516 , 240 , // 516 - mg-mg
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa8 , 1 , 518 , 240 , // 517 - mgh
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa8 , 1 , 518 , 240 , // 518 - mgh-mz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 520 , 240 , // 519 - mgo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 520 , 240 , // 520 - mgo-cm
+ 0x81 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb7 , 1 , 522 , 522 , // 521 - mi
+ 0x481 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb7 , 1 , 522 , 522 , // 522 - mi-nz
+ 0x2f , 0x4e3 , 0x362 , 0x2717, 0x1f4 , 0x4ca2, 1 , 524 , 524 , // 523 - mk
+ 0x42f , 0x4e3 , 0x362 , 0x2717, 0x1f4 , 0x4ca2, 1 , 524 , 524 , // 524 - mk-mk
+ 0x4c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 526 , 143 , // 525 - ml
+ 0x44c , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 526 , 143 , // 526 - ml-in
+ 0x50 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x9a , 1 , 529 , 529 , // 527 - mn
+ 0x7850 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x9a , 1 , 529 , 529 , // 528 - mn-cyrl
+ 0x450 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0x9a , 1 , 529 , 529 , // 529 - mn-mn
+ 0x7c50 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 531 , 531 , // 530 - mn-mong
+ 0x850 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2d , 1 , 531 , 531 , // 531 - mn-mong-cn
+ 0xc50 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9a , 1 , 532 , 532 , // 532 - mn-mong-mn
+ 0x58 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 534 , 187 , // 533 - mni
+ 0x458 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 534 , 187 , // 534 - mni-in
+ 0x7c , 0x4e4 , 0x352 , 0x2710, 0x25 , 0x27 , 1 , 536 , 240 , // 535 - moh
+ 0x47c , 0x4e4 , 0x352 , 0x2710, 0x25 , 0x27 , 1 , 536 , 240 , // 536 - moh-ca
+ 0x4e , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 538 , 143 , // 537 - mr
+ 0x44e , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 538 , 143 , // 538 - mr-in
+ 0x3e , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa7 , 1 , 541 , 541 , // 539 - ms
+ 0x83e , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x25 , 1 , 540 , 540 , // 540 - ms-bn
+ 0x43e , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa7 , 1 , 541 , 541 , // 541 - ms-my
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd7 , 1 , 542 , 240 , // 542 - ms-sg
+ 0x3a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa3 , 1 , 544 , 544 , // 543 - mt
+ 0x43a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa3 , 1 , 544 , 544 , // 544 - mt-mt
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 546 , 240 , // 545 - mua
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 546 , 240 , // 546 - mua-cm
+ 0x55 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x1b , 2 , 548 , 240 , // 547 - my
+ 0x455 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x1b , 2 , 548 , 240 , // 548 - my-mm
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 2 , 550 , 240 , // 549 - mzn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x74 , 2 , 550 , 240 , // 550 - mzn-ir
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xfe , 1 , 552 , 240 , // 551 - naq
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xfe , 1 , 552 , 240 , // 552 - naq-na
+ 0x7c14 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 554 , 554 , // 553 - nb
+ 0x414 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 554 , 554 , // 554 - nb-no
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xdc , 1 , 555 , 240 , // 555 - nb-sj
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 557 , 240 , // 556 - nd
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 557 , 240 , // 557 - nd-zw
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 559 , 240 , // 558 - nds
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x5e , 1 , 559 , 240 , // 559 - nds-de
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb0 , 1 , 560 , 240 , // 560 - nds-nl
+ 0x61 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb2 , 1 , 563 , 143 , // 561 - ne
+ 0x861 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 2 , 562 , 240 , // 562 - ne-in
+ 0x461 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xb2 , 1 , 563 , 143 , // 563 - ne-np
+ 0x13 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb0 , 1 , 569 , 569 , // 564 - nl
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x12e , 1 , 565 , 240 , // 565 - nl-aw
+ 0x813 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x15 , 1 , 566 , 566 , // 566 - nl-be
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x9a55d42, 1 , 567 , 240 , // 567 - nl-bq
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x111 , 1 , 568 , 240 , // 568 - nl-cw
+ 0x413 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb0 , 1 , 569 , 569 , // 569 - nl-nl
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xb5 , 1 , 570 , 240 , // 570 - nl-sr
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x78f7, 1 , 571 , 240 , // 571 - nl-sx
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 573 , 240 , // 572 - nmg
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 573 , 240 , // 573 - nmg-cm
+ 0x7814 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 575 , 575 , // 574 - nn
+ 0x814 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 575 , 575 , // 575 - nn-no
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 577 , 240 , // 576 - nnh
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 577 , 240 , // 577 - nnh-cm
+ 0x14 , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 554 , 554 , // 578 - no
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x64 , 2 , 580 , 143 , // 579 - nqo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x64 , 2 , 580 , 143 , // 580 - nqo-gn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 582 , 240 , // 581 - nr
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 582 , 240 , // 582 - nr-za
+ 0x6c , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 584 , 584 , // 583 - nso
+ 0x46c , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 584 , 584 , // 584 - nso-za
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x114 , 1 , 586 , 240 , // 585 - nus
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x114 , 1 , 586 , 240 , // 586 - nus-ss
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 588 , 240 , // 587 - nyn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 588 , 240 , // 588 - nyn-ug
+ 0x82 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 590 , 590 , // 589 - oc
+ 0x482 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x54 , 1 , 590 , 590 , // 590 - oc-fr
+ 0x72 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 592 , 240 , // 591 - om
+ 0x472 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 592 , 240 , // 592 - om-et
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 593 , 240 , // 593 - om-ke
+ 0x48 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 595 , 143 , // 594 - or
+ 0x448 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 595 , 143 , // 595 - or-in
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 597 , 240 , // 596 - os
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x58 , 1 , 597 , 240 , // 597 - os-ge
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xcb , 1 , 598 , 240 , // 598 - os-ru
+ 0x46 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 602 , 143 , // 599 - pa
+ 0x7c46 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 601 , 143 , // 600 - pa-arab
+ 0x846 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 601 , 143 , // 601 - pa-arab-pk
+ 0x446 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 602 , 143 , // 602 - pa-in
+ 0x79 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x993248, 1 , 604 , 145 , // 603 - pap
+ 0x479 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x993248, 1 , 604 , 145 , // 604 - pap-029
+ 0x15 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xbf , 1 , 606 , 606 , // 605 - pl
+ 0x415 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xbf , 1 , 606 , 606 , // 606 - pl-pl
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 608 , 240 , // 607 - prg
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 608 , 240 , // 608 - prg-001
+ 0x8c , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x3 , 2 , 610 , 143 , // 609 - prs
+ 0x48c , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x3 , 2 , 610 , 143 , // 610 - prs-af
+ 0x63 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3 , 2 , 612 , 143 , // 611 - ps
+ 0x463 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3 , 2 , 612 , 143 , // 612 - ps-af
+ 0x16 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x20 , 1 , 615 , 615 , // 613 - pt
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x9 , 1 , 614 , 240 , // 614 - pt-ao
+ 0x416 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x20 , 1 , 615 , 615 , // 615 - pt-br
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdf , 1 , 616 , 240 , // 616 - pt-ch
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x39 , 1 , 617 , 240 , // 617 - pt-cv
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x45 , 1 , 618 , 240 , // 618 - pt-gq
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc4 , 1 , 619 , 240 , // 619 - pt-gw
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x93 , 1 , 620 , 240 , // 620 - pt-lu
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x97 , 1 , 621 , 240 , // 621 - pt-mo
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xa8 , 1 , 622 , 240 , // 622 - pt-mz
+ 0x816 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xc1 , 1 , 623 , 623 , // 623 - pt-pt
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xe9 , 1 , 624 , 240 , // 624 - pt-st
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x6f60e7, 1 , 625 , 240 , // 625 - pt-tl
+ 0x901 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x7c , 1 , 626 , 190 , // 626 - qps-latn-x-sh
+ 0x501 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xf4 , 1 , 627 , 627 , // 627 - qps-ploc
+ 0x5fe , 0x3a4 , 0x3a4 , 0x2711, 0x4f42, 0x7a , 1 , 628 , 628 , // 628 - qps-ploca
+ 0x9ff , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xcd , 0 , 629 , 143 , // 629 - qps-plocm
+ 0x86 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x63 , 1 , 632 , 632 , // 630 - quc
+ 0x7c86 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x63 , 1 , 632 , 632 , // 631 - quc-latn
+ 0x486 , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x63 , 1 , 632 , 632 , // 632 - quc-latn-gt
+ 0x6b , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x1a , 1 , 634 , 634 , // 633 - quz
+ 0x46b , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x1a , 1 , 634 , 634 , // 634 - quz-bo
+ 0x86b , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0x42 , 1 , 635 , 635 , // 635 - quz-ec
+ 0xc6b , 0x4e4 , 0x352 , 0x2710, 0x4f3c, 0xbb , 1 , 636 , 636 , // 636 - quz-pe
+ 0x17 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0xdf , 1 , 638 , 638 , // 637 - rm
+ 0x417 , 0x4e4 , 0x352 , 0x2710, 0x4f31, 0xdf , 1 , 638 , 638 , // 638 - rm-ch
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x26 , 1 , 640 , 240 , // 639 - rn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x26 , 1 , 640 , 240 , // 640 - rn-bi
+ 0x18 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xc8 , 1 , 643 , 643 , // 641 - ro
+ 0x818 , 0x4e2 , 0x354 , 0x2 , 0x1f4 , 0x98 , 1 , 642 , 240 , // 642 - ro-md
+ 0x418 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xc8 , 1 , 643 , 643 , // 643 - ro-ro
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 645 , 240 , // 644 - rof
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 645 , 240 , // 645 - rof-tz
+ 0x19 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 651 , 651 , // 646 - ru
+ 0x1000 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0x1d , 1 , 647 , 240 , // 647 - ru-by
+ 0x1000 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0x82 , 1 , 648 , 240 , // 648 - ru-kg
+ 0x1000 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0x89 , 1 , 649 , 240 , // 649 - ru-kz
+ 0x819 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0x98 , 1 , 650 , 240 , // 650 - ru-md
+ 0x419 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 651 , 651 , // 651 - ru-ru
+ 0x1000 , 0x4e3 , 0x362 , 0x2 , 0x1f4 , 0xf1 , 1 , 652 , 240 , // 652 - ru-ua
+ 0x87 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xcc , 1 , 654 , 654 , // 653 - rw
+ 0x487 , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xcc , 1 , 654 , 654 , // 654 - rw-rw
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 656 , 240 , // 655 - rwk
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 656 , 240 , // 656 - rwk-tz
+ 0x4f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 658 , 143 , // 657 - sa
+ 0x44f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 658 , 143 , // 658 - sa-in
+ 0x85 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 660 , 660 , // 659 - sah
+ 0x485 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 660 , 660 , // 660 - sah-ru
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 662 , 240 , // 661 - saq
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 662 , 240 , // 662 - saq-ke
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 664 , 240 , // 663 - sbp
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 664 , 240 , // 664 - sbp-tz
+ 0x59 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 667 , 143 , // 665 - sd
+ 0x7c59 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 667 , 143 , // 666 - sd-arab
+ 0x859 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 2 , 667 , 143 , // 667 - sd-arab-pk
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 669 , 187 , // 668 - sd-deva
+ 0x459 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 669 , 187 , // 669 - sd-deva-in
+ 0x3b , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 672 , 672 , // 670 - se
+ 0xc3b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 671 , 671 , // 671 - se-fi
+ 0x43b , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 672 , 672 , // 672 - se-no
+ 0x83b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 673 , 673 , // 673 - se-se
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa8 , 1 , 675 , 240 , // 674 - seh
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa8 , 1 , 675 , 240 , // 675 - seh-mz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 677 , 240 , // 676 - ses
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9d , 1 , 677 , 240 , // 677 - ses-ml
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x37 , 1 , 679 , 240 , // 678 - sg
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x37 , 1 , 679 , 240 , // 679 - sg-cf
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 684 , 240 , // 680 - shi
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 682 , 240 , // 681 - shi-latn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 682 , 240 , // 682 - shi-latn-ma
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 684 , 240 , // 683 - shi-tfng
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 684 , 240 , // 684 - shi-tfng-ma
+ 0x5b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2a , 1 , 686 , 143 , // 685 - si
+ 0x45b , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2a , 1 , 686 , 143 , // 686 - si-lk
+ 0x1b , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x8f , 1 , 688 , 688 , // 687 - sk
+ 0x41b , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x8f , 1 , 688 , 688 , // 688 - sk-sk
+ 0x24 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xd4 , 1 , 690 , 690 , // 689 - sl
+ 0x424 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xd4 , 1 , 690 , 690 , // 690 - sl-si
+ 0x783b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 693 , 693 , // 691 - sma
+ 0x183b , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 692 , 692 , // 692 - sma-no
+ 0x1c3b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 693 , 693 , // 693 - sma-se
+ 0x7c3b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 696 , 696 , // 694 - smj
+ 0x103b , 0x4e4 , 0x352 , 0x2710, 0x4f35, 0xb1 , 1 , 695 , 695 , // 695 - smj-no
+ 0x143b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 696 , 696 , // 696 - smj-se
+ 0x703b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 698 , 698 , // 697 - smn
+ 0x243b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 698 , 698 , // 698 - smn-fi
+ 0x743b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 700 , 700 , // 699 - sms
+ 0x203b , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 700 , 700 , // 700 - sms-fi
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 703 , 240 , // 701 - sn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 703 , 240 , // 702 - sn-latn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x108 , 1 , 703 , 240 , // 703 - sn-latn-zw
+ 0x77 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd8 , 1 , 708 , 240 , // 704 - so
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3e , 1 , 705 , 240 , // 705 - so-dj
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 706 , 240 , // 706 - so-et
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 707 , 240 , // 707 - so-ke
+ 0x477 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd8 , 1 , 708 , 240 , // 708 - so-so
+ 0x1c , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x6 , 1 , 710 , 710 , // 709 - sq
+ 0x41c , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x6 , 1 , 710 , 710 , // 710 - sq-al
+ 0x1000 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x4ca2, 1 , 711 , 240 , // 711 - sq-mk
+ 0x1000 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0x974941, 1 , 712 , 240 , // 712 - sq-xk
+ 0x7c1a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10f , 1 , 724 , 724 , // 713 - sr
+ 0x6c1a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x10f , 1 , 718 , 718 , // 714 - sr-cyrl
+ 0x1c1a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x19 , 1 , 715 , 715 , // 715 - sr-cyrl-ba
+ 0xc1a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x10d , 1 , 716 , 716 , // 716 - sr-cyrl-cs
+ 0x301a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x10e , 1 , 717 , 717 , // 717 - sr-cyrl-me
+ 0x281a , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x10f , 1 , 718 , 718 , // 718 - sr-cyrl-rs
+ 0x1000 , 0x4e3 , 0x357 , 0x2717, 0x5221, 0x974941, 1 , 719 , 240 , // 719 - sr-cyrl-xk
+ 0x701a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10f , 1 , 724 , 724 , // 720 - sr-latn
+ 0x181a , 0x4e2 , 0x354 , 0x2762, 0x366 , 0x19 , 1 , 721 , 721 , // 721 - sr-latn-ba
+ 0x81a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10d , 1 , 722 , 722 , // 722 - sr-latn-cs
+ 0x2c1a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10e , 1 , 723 , 723 , // 723 - sr-latn-me
+ 0x241a , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x10f , 1 , 724 , 724 , // 724 - sr-latn-rs
+ 0x1000 , 0x4e2 , 0x354 , 0x272d, 0x1f4 , 0x974941, 1 , 725 , 240 , // 725 - sr-latn-xk
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 728 , 240 , // 726 - ss
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x104 , 1 , 727 , 240 , // 727 - ss-sz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 728 , 240 , // 728 - ss-za
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 730 , 240 , // 729 - ssy
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 730 , 240 , // 730 - ssy-er
+ 0x30 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 733 , 240 , // 731 - st
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x92 , 1 , 732 , 240 , // 732 - st-ls
+ 0x430 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 733 , 240 , // 733 - st-za
+ 0x1d , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 737 , 737 , // 734 - sv
+ 0x1000 , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x9906f5, 1 , 735 , 240 , // 735 - sv-ax
+ 0x81d , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0x4d , 1 , 736 , 736 , // 736 - sv-fi
+ 0x41d , 0x4e4 , 0x352 , 0x2710, 0x4f36, 0xdd , 1 , 737 , 737 , // 737 - sv-se
+ 0x41 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x81 , 1 , 740 , 740 , // 738 - sw
+ 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x2c , 1 , 739 , 740 , // 739 - sw-cd
+ 0x441 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x81 , 1 , 740 , 740 , // 740 - sw-ke
+ 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xef , 1 , 741 , 240 , // 741 - sw-tz
+ 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0xf0 , 1 , 742 , 240 , // 742 - sw-ug
+ 0x1000 , 0x0 , 0x1 , 0x0 , 0x1f4 , 0x2c , 1 , 744 , 240 , // 743 - swc
+ 0x1000 , 0x0 , 0x1 , 0x0 , 0x1f4 , 0x2c , 1 , 744 , 240 , // 744 - swc-cd
+ 0x5a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xde , 1 , 746 , 143 , // 745 - syr
+ 0x45a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xde , 1 , 746 , 143 , // 746 - syr-sy
+ 0x49 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 748 , 143 , // 747 - ta
+ 0x449 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 748 , 143 , // 748 - ta-in
+ 0x849 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x2a , 1 , 749 , 143 , // 749 - ta-lk
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xa7 , 1 , 750 , 240 , // 750 - ta-my
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd7 , 1 , 751 , 240 , // 751 - ta-sg
+ 0x4a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 753 , 143 , // 752 - te
+ 0x44a , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x71 , 1 , 753 , 143 , // 753 - te-in
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 756 , 240 , // 754 - teo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x81 , 1 , 755 , 240 , // 755 - teo-ke
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 756 , 240 , // 756 - teo-ug
+ 0x28 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xe4 , 1 , 759 , 759 , // 757 - tg
+ 0x7c28 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xe4 , 1 , 759 , 759 , // 758 - tg-cyrl
+ 0x428 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xe4 , 1 , 759 , 759 , // 759 - tg-cyrl-tj
+ 0x1e , 0x36a , 0x36a , 0x2725, 0x5166, 0xe3 , 1 , 761 , 143 , // 760 - th
+ 0x41e , 0x36a , 0x36a , 0x2725, 0x5166, 0xe3 , 1 , 761 , 143 , // 761 - th-th
+ 0x73 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 763 , 143 , // 762 - ti
+ 0x873 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 763 , 143 , // 763 - ti-er
+ 0x473 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 764 , 143 , // 764 - ti-et
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 766 , 240 , // 765 - tig
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x47 , 1 , 766 , 240 , // 766 - tig-er
+ 0x42 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xee , 1 , 768 , 768 , // 767 - tk
+ 0x442 , 0x4e2 , 0x354 , 0x272d, 0x5190, 0xee , 1 , 768 , 768 , // 768 - tk-tm
+ 0x32 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 771 , 771 , // 769 - tn
+ 0x832 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0x13 , 1 , 770 , 770 , // 770 - tn-bw
+ 0x432 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 771 , 771 , // 771 - tn-za
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xe7 , 1 , 773 , 240 , // 772 - to
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xe7 , 1 , 773 , 240 , // 773 - to-to
+ 0x1f , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0xeb , 1 , 776 , 776 , // 774 - tr
+ 0x1000 , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0x3b , 1 , 775 , 240 , // 775 - tr-cy
+ 0x41f , 0x4e6 , 0x359 , 0x2761, 0x51a9, 0xeb , 1 , 776 , 776 , // 776 - tr-tr
+ 0x31 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 778 , 240 , // 777 - ts
+ 0x431 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 778 , 240 , // 778 - ts-za
+ 0x44 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 780 , 780 , // 779 - tt
+ 0x444 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xcb , 1 , 780 , 780 , // 780 - tt-ru
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xad , 1 , 782 , 240 , // 781 - twq
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xad , 1 , 782 , 240 , // 782 - twq-ne
+ 0x5f , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x4 , 1 , 787 , 787 , // 783 - tzm
+ 0x1000 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x9f , 1 , 785 , 240 , // 784 - tzm-arab
+ 0x45f , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x9f , 1 , 785 , 240 , // 785 - tzm-arab-ma
+ 0x7c5f , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x4 , 1 , 787 , 787 , // 786 - tzm-latn
+ 0x85f , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0x4 , 1 , 787 , 787 , // 787 - tzm-latn-dz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 788 , 240 , // 788 - tzm-latn-ma
+ 0x785f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 790 , 316 , // 789 - tzm-tfng
+ 0x105f , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 790 , 316 , // 790 - tzm-tfng-ma
+ 0x80 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x2d , 1 , 792 , 143 , // 791 - ug
+ 0x480 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0x2d , 1 , 792 , 143 , // 792 - ug-cn
+ 0x22 , 0x4e3 , 0x362 , 0x2721, 0x1f4 , 0xf1 , 1 , 794 , 794 , // 793 - uk
+ 0x422 , 0x4e3 , 0x362 , 0x2721, 0x1f4 , 0xf1 , 1 , 794 , 794 , // 794 - uk-ua
+ 0x20 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 1 , 797 , 143 , // 795 - ur
+ 0x820 , 0x4e8 , 0x2d0 , 0x2 , 0x1f4 , 0x71 , 2 , 796 , 240 , // 796 - ur-in
+ 0x420 , 0x4e8 , 0x2d0 , 0x2714, 0x4fc4, 0xbe , 1 , 797 , 143 , // 797 - ur-pk
+ 0x43 , 0x4e6 , 0x359 , 0x272d, 0x1f4 , 0xf7 , 1 , 804 , 804 , // 798 - uz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3 , 2 , 800 , 240 , // 799 - uz-arab
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x3 , 2 , 800 , 240 , // 800 - uz-arab-af
+ 0x7843 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xf7 , 1 , 802 , 802 , // 801 - uz-cyrl
+ 0x843 , 0x4e3 , 0x362 , 0x2717, 0x5190, 0xf7 , 1 , 802 , 802 , // 802 - uz-cyrl-uz
+ 0x7c43 , 0x4e6 , 0x359 , 0x272d, 0x1f4 , 0xf7 , 1 , 804 , 804 , // 803 - uz-latn
+ 0x443 , 0x4e6 , 0x359 , 0x272d, 0x1f4 , 0xf7 , 1 , 804 , 804 , // 804 - uz-latn-uz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 809 , 240 , // 805 - vai
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 807 , 240 , // 806 - vai-latn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 807 , 240 , // 807 - vai-latn-lr
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 809 , 240 , // 808 - vai-vaii
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x8e , 1 , 809 , 240 , // 809 - vai-vaii-lr
+ 0x33 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 811 , 240 , // 810 - ve
+ 0x433 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xd1 , 1 , 811 , 240 , // 811 - ve-za
+ 0x2a , 0x4ea , 0x4ea , 0x2710, 0x1f4 , 0xfb , 1 , 813 , 143 , // 812 - vi
+ 0x42a , 0x4ea , 0x4ea , 0x2710, 0x1f4 , 0xfb , 1 , 813 , 143 , // 813 - vi-vn
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 815 , 240 , // 814 - vo
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 815 , 240 , // 815 - vo-001
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 817 , 240 , // 816 - vun
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xef , 1 , 817 , 240 , // 817 - vun-tz
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdf , 1 , 819 , 240 , // 818 - wae
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xdf , 1 , 819 , 240 , // 819 - wae-ch
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 821 , 240 , // 820 - wal
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x49 , 1 , 821 , 240 , // 821 - wal-et
+ 0x88 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 823 , 823 , // 822 - wo
+ 0x488 , 0x4e4 , 0x352 , 0x2710, 0x4f49, 0xd2 , 1 , 823 , 823 , // 823 - wo-sn
+ 0x1007f, 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xf4 , 1 , -1 , -1 , // 824 - x-iv_mathan
+ 0x34 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 826 , 826 , // 825 - xh
+ 0x434 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 826 , 826 , // 826 - xh-za
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 828 , 240 , // 827 - xog
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0xf0 , 1 , 828 , 240 , // 828 - xog-ug
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 830 , 240 , // 829 - yav
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x31 , 1 , 830 , 240 , // 830 - yav-cm
+ 0x3d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 832 , 240 , // 831 - yi
+ 0x43d , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x989e, 1 , 832 , 240 , // 832 - yi-001
+ 0x6a , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 835 , 835 , // 833 - yo
+ 0x1000 , 0x4e4 , 0x1b5 , 0x2710, 0x1f4 , 0x1c , 1 , 834 , 240 , // 834 - yo-bj
+ 0x46a , 0x4e4 , 0x1b5 , 0x2710, 0x25 , 0xaf , 1 , 835 , 835 , // 835 - yo-ng
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x68 , 1 , 837 , 240 , // 836 - yue
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x68 , 1 , 837 , 240 , // 837 - yue-hk
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 840 , 316 , // 838 - zgh
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 840 , 316 , // 839 - zgh-tfng
+ 0x1000 , 0x0 , 0x1 , 0x2 , 0x1f4 , 0x9f , 1 , 840 , 316 , // 840 - zgh-tfng-ma
+ 0x7804 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 841 - zh
+ 0x4 , 0x3a8 , 0x3a8 , 0x0 , 0x1f4 , 0x2d , 1 , 844 , 844 , // 842 - zh-chs
+ 0x7c04 , 0x3b6 , 0x3b6 , 0x0 , 0x1f4 , 0x68 , 1 , 851 , 851 , // 843 - zh-cht
+ 0x804 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 844 - zh-cn
+ 0x50804, 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 845 - zh-cn_phoneb
+ 0x20804, 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 846 - zh-cn_stroke
+ 0x4 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x2d , 1 , 844 , 844 , // 847 - zh-hans
+ 0x1000 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x68 , 1 , 848 , 240 , // 848 - zh-hans-hk
+ 0x1000 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0x97 , 1 , 849 , 240 , // 849 - zh-hans-mo
+ 0x7c04 , 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x68 , 1 , 851 , 851 , // 850 - zh-hant
+ 0xc04 , 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x68 , 1 , 851 , 851 , // 851 - zh-hk
+ 0x40c04, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x68 , 1 , 851 , 851 , // 852 - zh-hk_radstr
+ 0x1404 , 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x97 , 1 , 853 , 853 , // 853 - zh-mo
+ 0x41404, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x97 , 1 , 853 , 853 , // 854 - zh-mo_radstr
+ 0x21404, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0x97 , 1 , 853 , 853 , // 855 - zh-mo_stroke
+ 0x1004 , 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0xd7 , 1 , 856 , 856 , // 856 - zh-sg
+ 0x51004, 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0xd7 , 1 , 856 , 856 , // 857 - zh-sg_phoneb
+ 0x21004, 0x3a8 , 0x3a8 , 0x2718, 0x1f4 , 0xd7 , 1 , 856 , 856 , // 858 - zh-sg_stroke
+ 0x404 , 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0xed , 1 , 859 , 859 , // 859 - zh-tw
+ 0x30404, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0xed , 1 , 859 , 859 , // 860 - zh-tw_pronun
+ 0x40404, 0x3b6 , 0x3b6 , 0x2712, 0x1f4 , 0xed , 1 , 859 , 859 , // 861 - zh-tw_radstr
+ 0x35 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 863 , 863 , // 862 - zu
+ 0x435 , 0x4e4 , 0x352 , 0x2710, 0x1f4 , 0xd1 , 1 , 863 , 863 , // 863 - zu-za
+ };
+
+ // s_lcids list all supported lcids. used to binary search and we use the index of the matched lcid to
+ // get the index in s_localeNamesIndices using s_lcidToCultureNameIndices
+ private static readonly int[] s_lcids = new int[]
+ {
+ // Lcid , index - index in c_localeNames
+ 0x1 , // 0 - 52
+ 0x2 , // 1 - 301
+ 0x3 , // 2 - 421
+ 0x4 , // 3 - 4139
+ 0x5 , // 4 - 502
+ 0x6 , // 5 - 523
+ 0x7 , // 6 - 544
+ 0x8 , // 7 - 664
+ 0x9 , // 8 - 676
+ 0xa , // 9 - 1214
+ 0xb , // 10 - 1423
+ 0xc , // 11 - 1451
+ 0xd , // 12 - 1825
+ 0xe , // 13 - 1860
+ 0xf , // 14 - 1929
+ 0x10 , // 15 - 1936
+ 0x11 , // 16 - 1989
+ 0x12 , // 17 - 2179
+ 0x13 , // 18 - 2685
+ 0x14 , // 19 - 2747
+ 0x15 , // 20 - 2864
+ 0x16 , // 21 - 2897
+ 0x17 , // 22 - 3041
+ 0x18 , // 23 - 3055
+ 0x19 , // 24 - 3076
+ 0x1a , // 25 - 1839
+ 0x1b , // 26 - 3284
+ 0x1c , // 27 - 3387
+ 0x1d , // 28 - 3553
+ 0x1e , // 29 - 3673
+ 0x1f , // 30 - 3727
+ 0x20 , // 31 - 3847
+ 0x21 , // 32 - 1908
+ 0x22 , // 33 - 3840
+ 0x23 , // 34 - 276
+ 0x24 , // 35 - 3291
+ 0x25 , // 36 - 1354
+ 0x26 , // 37 - 2429
+ 0x27 , // 38 - 2397
+ 0x28 , // 39 - 3654
+ 0x29 , // 40 - 1377
+ 0x2a , // 41 - 3960
+ 0x2b , // 42 - 1879
+ 0x2c , // 43 - 224
+ 0x2d , // 44 - 1361
+ 0x2e , // 45 - 1851
+ 0x2f , // 46 - 2501
+ 0x30 , // 47 - 3541
+ 0x31 , // 48 - 3739
+ 0x32 , // 49 - 3708
+ 0x33 , // 50 - 3953
+ 0x34 , // 51 - 4020
+ 0x35 , // 52 - 4277
+ 0x36 , // 53 - 17
+ 0x37 , // 54 - 2062
+ 0x38 , // 55 - 1439
+ 0x39 , // 56 - 1832
+ 0x3a , // 57 - 2598
+ 0x3b , // 58 - 3194
+ 0x3c , // 59 - 1705
+ 0x3d , // 60 - 4045
+ 0x3e , // 61 - 2581
+ 0x3f , // 62 - 2133
+ 0x40 , // 63 - 2306
+ 0x41 , // 64 - 3570
+ 0x42 , // 65 - 3701
+ 0x43 , // 66 - 3859
+ 0x44 , // 67 - 3746
+ 0x45 , // 68 - 336
+ 0x46 , // 69 - 2830
+ 0x47 , // 70 - 1754
+ 0x48 , // 71 - 2811
+ 0x49 , // 72 - 3610
+ 0x4a , // 73 - 3632
+ 0x4b , // 74 - 2172
+ 0x4c , // 75 - 2508
+ 0x4d , // 76 - 199
+ 0x4e , // 77 - 2574
+ 0x4f , // 78 - 3124
+ 0x50 , // 79 - 2515
+ 0x51 , // 80 - 348
+ 0x52 , // 81 - 516
+ 0x53 , // 82 - 2165
+ 0x54 , // 83 - 2375
+ 0x55 , // 84 - 2614
+ 0x56 , // 85 - 1719
+ 0x57 , // 86 - 2191
+ 0x58 , // 87 - 2556
+ 0x59 , // 88 - 3158
+ 0x5a , // 89 - 3601
+ 0x5b , // 90 - 3277
+ 0x5c , // 91 - 473
+ 0x5d , // 92 - 1953
+ 0x5e , // 93 - 45
+ 0x5f , // 94 - 3762
+ 0x60 , // 95 - 2207
+ 0x61 , // 96 - 2673
+ 0x62 , // 97 - 1698
+ 0x63 , // 98 - 2890
+ 0x64 , // 99 - 1430
+ 0x65 , // 100 - 620
+ 0x66 , // 101 - 308
+ 0x67 , // 102 - 1384
+ 0x68 , // 103 - 1777
+ 0x69 , // 104 - 1899
+ 0x6a , // 105 - 4053
+ 0x6b , // 106 - 3020
+ 0x6c , // 107 - 2765
+ 0x6d , // 108 - 260
+ 0x6e , // 109 - 2330
+ 0x6f , // 110 - 2149
+ 0x70 , // 111 - 1915
+ 0x71 , // 112 - 2200
+ 0x72 , // 113 - 2799
+ 0x73 , // 114 - 3680
+ 0x74 , // 115 - 1726
+ 0x75 , // 116 - 1816
+ 0x76 , // 117 - 2313
+ 0x77 , // 118 - 3365
+ 0x78 , // 119 - 1922
+ 0x79 , // 120 - 2854
+ 0x7a , // 121 - 190
+ 0x7c , // 122 - 2565
+ 0x7e , // 123 - 360
+ 0x80 , // 124 - 3833
+ 0x81 , // 125 - 2494
+ 0x82 , // 126 - 2792
+ 0x83 , // 127 - 495
+ 0x84 , // 128 - 1733
+ 0x85 , // 129 - 3131
+ 0x86 , // 130 - 2998
+ 0x87 , // 131 - 3108
+ 0x88 , // 132 - 4002
+ 0x8c , // 133 - 2881
+ 0x91 , // 134 - 1712
+ 0x92 , // 135 - 2270
+ 0x401 , // 136 - 150
+ 0x402 , // 137 - 303
+ 0x403 , // 138 - 428
+ 0x404 , // 139 - 4248
+ 0x405 , // 140 - 504
+ 0x406 , // 141 - 525
+ 0x407 , // 142 - 561
+ 0x408 , // 143 - 671
+ 0x409 , // 144 - 1161
+ 0x40a , // 145 - 1272
+ 0x40b , // 146 - 1425
+ 0x40c , // 147 - 1529
+ 0x40d , // 148 - 1827
+ 0x40e , // 149 - 1862
+ 0x40f , // 150 - 1931
+ 0x410 , // 151 - 1943
+ 0x411 , // 152 - 1991
+ 0x412 , // 153 - 2186
+ 0x413 , // 154 - 2707
+ 0x414 , // 155 - 2641
+ 0x415 , // 156 - 2866
+ 0x416 , // 157 - 2904
+ 0x417 , // 158 - 3043
+ 0x418 , // 159 - 3062
+ 0x419 , // 160 - 3098
+ 0x41a , // 161 - 1846
+ 0x41b , // 162 - 3286
+ 0x41c , // 163 - 3389
+ 0x41d , // 164 - 3565
+ 0x41e , // 165 - 3675
+ 0x41f , // 166 - 3734
+ 0x420 , // 167 - 3854
+ 0x421 , // 168 - 1910
+ 0x422 , // 169 - 3842
+ 0x423 , // 170 - 278
+ 0x424 , // 171 - 3293
+ 0x425 , // 172 - 1356
+ 0x426 , // 173 - 2431
+ 0x427 , // 174 - 2399
+ 0x428 , // 175 - 3663
+ 0x429 , // 176 - 1379
+ 0x42a , // 177 - 3962
+ 0x42b , // 178 - 1881
+ 0x42c , // 179 - 250
+ 0x42d , // 180 - 1363
+ 0x42e , // 181 - 1854
+ 0x42f , // 182 - 2503
+ 0x430 , // 183 - 3548
+ 0x431 , // 184 - 3741
+ 0x432 , // 185 - 3715
+ 0x433 , // 186 - 3955
+ 0x434 , // 187 - 4022
+ 0x435 , // 188 - 4279
+ 0x436 , // 189 - 24
+ 0x437 , // 190 - 2064
+ 0x438 , // 191 - 1446
+ 0x439 , // 192 - 1834
+ 0x43a , // 193 - 2600
+ 0x43b , // 194 - 3201
+ 0x43d , // 195 - 4047
+ 0x43e , // 196 - 2588
+ 0x43f , // 197 - 2135
+ 0x440 , // 198 - 2308
+ 0x441 , // 199 - 3577
+ 0x442 , // 200 - 3703
+ 0x443 , // 201 - 3902
+ 0x444 , // 202 - 3748
+ 0x445 , // 203 - 343
+ 0x446 , // 204 - 2849
+ 0x447 , // 205 - 1756
+ 0x448 , // 206 - 2813
+ 0x449 , // 207 - 3612
+ 0x44a , // 208 - 3634
+ 0x44b , // 209 - 2174
+ 0x44c , // 210 - 2510
+ 0x44d , // 211 - 201
+ 0x44e , // 212 - 2576
+ 0x44f , // 213 - 3126
+ 0x450 , // 214 - 2524
+ 0x451 , // 215 - 350
+ 0x452 , // 216 - 518
+ 0x453 , // 217 - 2167
+ 0x454 , // 218 - 2377
+ 0x455 , // 219 - 2616
+ 0x456 , // 220 - 1721
+ 0x457 , // 221 - 2194
+ 0x458 , // 222 - 2559
+ 0x459 , // 223 - 3184
+ 0x45a , // 224 - 3604
+ 0x45b , // 225 - 3279
+ 0x45c , // 226 - 484
+ 0x45d , // 227 - 1962
+ 0x45e , // 228 - 47
+ 0x45f , // 229 - 3773
+ 0x460 , // 230 - 2209
+ 0x461 , // 231 - 2680
+ 0x462 , // 232 - 1700
+ 0x463 , // 233 - 2892
+ 0x464 , // 234 - 1433
+ 0x465 , // 235 - 622
+ 0x466 , // 236 - 311
+ 0x467 , // 237 - 1418
+ 0x468 , // 238 - 1806
+ 0x469 , // 239 - 1902
+ 0x46a , // 240 - 4060
+ 0x46b , // 241 - 3023
+ 0x46c , // 242 - 2768
+ 0x46d , // 243 - 262
+ 0x46e , // 244 - 2332
+ 0x46f , // 245 - 2151
+ 0x470 , // 246 - 1917
+ 0x471 , // 247 - 2202
+ 0x472 , // 248 - 2801
+ 0x473 , // 249 - 3687
+ 0x474 , // 250 - 1728
+ 0x475 , // 251 - 1819
+ 0x476 , // 252 - 2315
+ 0x477 , // 253 - 3382
+ 0x478 , // 254 - 1924
+ 0x479 , // 255 - 2857
+ 0x47a , // 256 - 193
+ 0x47c , // 257 - 2568
+ 0x47e , // 258 - 362
+ 0x480 , // 259 - 3835
+ 0x481 , // 260 - 2496
+ 0x482 , // 261 - 2794
+ 0x483 , // 262 - 497
+ 0x484 , // 263 - 1742
+ 0x485 , // 264 - 3134
+ 0x486 , // 265 - 3009
+ 0x487 , // 266 - 3110
+ 0x488 , // 267 - 4004
+ 0x48c , // 268 - 2884
+ 0x491 , // 269 - 1714
+ 0x492 , // 270 - 2279
+ 0x501 , // 271 - 2972
+ 0x5fe , // 272 - 2980
+ 0x801 , // 273 - 95
+ 0x803 , // 274 - 433
+ 0x804 , // 275 - 4110
+ 0x807 , // 276 - 556
+ 0x809 , // 277 - 831
+ 0x80a , // 278 - 1299
+ 0x80c , // 279 - 1459
+ 0x810 , // 280 - 1938
+ 0x813 , // 281 - 2692
+ 0x814 , // 282 - 2733
+ 0x816 , // 283 - 2944
+ 0x818 , // 284 - 3057
+ 0x819 , // 285 - 3093
+ 0x81a , // 286 - 3480
+ 0x81d , // 287 - 3560
+ 0x820 , // 288 - 3849
+ 0x82c , // 289 - 233
+ 0x82e , // 290 - 605
+ 0x832 , // 291 - 3710
+ 0x83b , // 292 - 3206
+ 0x83c , // 293 - 1707
+ 0x83e , // 294 - 2583
+ 0x843 , // 295 - 3885
+ 0x845 , // 296 - 338
+ 0x846 , // 297 - 2839
+ 0x849 , // 298 - 3617
+ 0x850 , // 299 - 2536
+ 0x859 , // 300 - 3167
+ 0x85d , // 301 - 1979
+ 0x85f , // 302 - 3792
+ 0x860 , // 303 - 2233
+ 0x861 , // 304 - 2675
+ 0x867 , // 305 - 1403
+ 0x86b , // 306 - 3029
+ 0x873 , // 307 - 3682
+ 0x901 , // 308 - 2959
+ 0x9ff , // 309 - 2989
+ 0xc01 , // 310 - 80
+ 0xc04 , // 311 - 4173
+ 0xc07 , // 312 - 546
+ 0xc09 , // 313 - 716
+ 0xc0a , // 314 - 1267
+ 0xc0c , // 315 - 1484
+ 0xc1a , // 316 - 3423
+ 0xc3b , // 317 - 3196
+ 0xc50 , // 318 - 2546
+ 0xc51 , // 319 - 638
+ 0xc6b , // 320 - 3035
+ 0x1001 , // 321 - 120
+ 0x1004 , // 322 - 4219
+ 0x1007 , // 323 - 588
+ 0x1009 , // 324 - 756
+ 0x100a , // 325 - 1289
+ 0x100c , // 326 - 1504
+ 0x101a , // 327 - 1841
+ 0x103b , // 328 - 3316
+ 0x105f , // 329 - 3822
+ 0x1401 , // 330 - 75
+ 0x1404 , // 331 - 4190
+ 0x1407 , // 332 - 583
+ 0x1409 , // 333 - 1026
+ 0x140a , // 334 - 1247
+ 0x140c , // 335 - 1569
+ 0x141a , // 336 - 402
+ 0x143b , // 337 - 3322
+ 0x1801 , // 338 - 125
+ 0x1809 , // 339 - 881
+ 0x180a , // 340 - 1309
+ 0x180c , // 341 - 1579
+ 0x181a , // 342 - 3470
+ 0x183b , // 343 - 3301
+ 0x1c01 , // 344 - 180
+ 0x1c09 , // 345 - 1191
+ 0x1c0a , // 346 - 1257
+ 0x1c0c , // 347 - 1453
+ 0x1c1a , // 348 - 3413
+ 0x1c3b , // 349 - 3307
+ 0x2001 , // 350 - 135
+ 0x2009 , // 351 - 911
+ 0x200a , // 352 - 1349
+ 0x200c , // 353 - 1634
+ 0x201a , // 354 - 385
+ 0x203b , // 355 - 3340
+ 0x2401 , // 356 - 185
+ 0x2409 , // 357 - 684
+ 0x240a , // 358 - 1242
+ 0x240c , // 359 - 1489
+ 0x241a , // 360 - 3500
+ 0x243b , // 361 - 3331
+ 0x2801 , // 362 - 170
+ 0x2809 , // 363 - 751
+ 0x280a , // 364 - 1314
+ 0x280c , // 365 - 1649
+ 0x281a , // 366 - 3443
+ 0x2c01 , // 367 - 100
+ 0x2c09 , // 368 - 1136
+ 0x2c0a , // 369 - 1222
+ 0x2c0c , // 370 - 1514
+ 0x2c1a , // 371 - 3490
+ 0x3001 , // 372 - 115
+ 0x3009 , // 373 - 1201
+ 0x300a , // 374 - 1262
+ 0x300c , // 375 - 1509
+ 0x301a , // 376 - 3433
+ 0x3401 , // 377 - 110
+ 0x3409 , // 378 - 1036
+ 0x340a , // 379 - 1237
+ 0x340c , // 380 - 1594
+ 0x3801 , // 381 - 60
+ 0x3809 , // 382 - 876
+ 0x380a , // 383 - 1344
+ 0x380c , // 384 - 1574
+ 0x3c01 , // 385 - 65
+ 0x3c09 , // 386 - 871
+ 0x3c0a , // 387 - 1329
+ 0x3c0c , // 388 - 1559
+ 0x4001 , // 389 - 145
+ 0x4009 , // 390 - 896
+ 0x400a , // 391 - 1227
+ 0x4409 , // 392 - 991
+ 0x440a , // 393 - 1334
+ 0x4809 , // 394 - 1086
+ 0x480a , // 395 - 1294
+ 0x4c0a , // 396 - 1304
+ 0x500a , // 397 - 1324
+ 0x540a , // 398 - 1339
+ 0x580a , // 399 - 1216
+ 0x5c0a , // 400 - 1252
+ 0x641a , // 401 - 378
+ 0x681a , // 402 - 395
+ 0x6c1a , // 403 - 3406
+ 0x701a , // 404 - 3463
+ 0x703b , // 405 - 3328
+ 0x742c , // 406 - 226
+ 0x743b , // 407 - 3337
+ 0x7804 , // 408 - 4096
+ 0x7814 , // 409 - 2731
+ 0x781a , // 410 - 376
+ 0x782c , // 411 - 243
+ 0x783b , // 412 - 3298
+ 0x7843 , // 413 - 3878
+ 0x7850 , // 414 - 2517
+ 0x785d , // 415 - 1955
+ 0x785f , // 416 - 3814
+ 0x7c04 , // 417 - 4166
+ 0x7c14 , // 418 - 2639
+ 0x7c1a , // 419 - 3404
+ 0x7c28 , // 420 - 3656
+ 0x7c2e , // 421 - 602
+ 0x7c3b , // 422 - 3313
+ 0x7c43 , // 423 - 3895
+ 0x7c46 , // 424 - 2832
+ 0x7c50 , // 425 - 2529
+ 0x7c59 , // 426 - 3160
+ 0x7c5c , // 427 - 476
+ 0x7c5d , // 428 - 1972
+ 0x7c5f , // 429 - 3784
+ 0x7c67 , // 430 - 1396
+ 0x7c68 , // 431 - 1779
+ 0x7c86 , // 432 - 3001
+ 0x7c92 , // 433 - 2272
+ 0x1007f, // 434 - 4009
+ 0x10407, // 435 - 566
+ 0x1040e, // 436 - 1867
+ 0x10437, // 437 - 2069
+ 0x20804, // 438 - 4127
+ 0x21004, // 439 - 4236
+ 0x21404, // 440 - 4207
+ 0x30404, // 441 - 4253
+ 0x40404, // 442 - 4265
+ 0x40411, // 443 - 1996
+ 0x40c04, // 444 - 4178
+ 0x41404, // 445 - 4195
+ 0x50804, // 446 - 4115
+ 0x51004 // 447 - 4224
+ };
+ // each element in s_lcidToCultureNameIndices is index to s_localeNamesIndices
+ private static readonly int[] s_lcidToCultureNameIndices = new int[]
+ {
+ // Index to s_localeNamesIndices, index to this array - lcid - index to the c_localeNames
+ 13 , // 0 - 1 - 52
+ 64 , // 1 - 2 - 301
+ 88 , // 2 - 3 - 421
+ 847 , // 3 - 4 - 4139
+ 103 , // 4 - 5 - 502
+ 109 , // 5 - 6 - 523
+ 114 , // 6 - 7 - 544
+ 140 , // 7 - 8 - 664
+ 143 , // 8 - 9 - 676
+ 251 , // 9 - a - 1214
+ 293 , // 10 - b - 1423
+ 300 , // 11 - c - 1451
+ 377 , // 12 - d - 1825
+ 386 , // 13 - e - 1860
+ 402 , // 14 - f - 1929
+ 404 , // 15 - 10 - 1936
+ 413 , // 16 - 11 - 1989
+ 452 , // 17 - 12 - 2179
+ 564 , // 18 - 13 - 2685
+ 578 , // 19 - 14 - 2747
+ 605 , // 20 - 15 - 2864
+ 613 , // 21 - 16 - 2897
+ 637 , // 22 - 17 - 3041
+ 641 , // 23 - 18 - 3055
+ 646 , // 24 - 19 - 3076
+ 381 , // 25 - 1a - 1839
+ 687 , // 26 - 1b - 3284
+ 709 , // 27 - 1c - 3387
+ 734 , // 28 - 1d - 3553
+ 760 , // 29 - 1e - 3673
+ 774 , // 30 - 1f - 3727
+ 795 , // 31 - 20 - 3847
+ 396 , // 32 - 21 - 1908
+ 793 , // 33 - 22 - 3840
+ 58 , // 34 - 23 - 276
+ 689 , // 35 - 24 - 3291
+ 278 , // 36 - 25 - 1354
+ 506 , // 37 - 26 - 2429
+ 498 , // 38 - 27 - 2397
+ 757 , // 39 - 28 - 3654
+ 284 , // 40 - 29 - 1377
+ 812 , // 41 - 2a - 3960
+ 389 , // 42 - 2b - 1879
+ 49 , // 43 - 2c - 224
+ 280 , // 44 - 2d - 1361
+ 384 , // 45 - 2e - 1851
+ 523 , // 46 - 2f - 2501
+ 731 , // 47 - 30 - 3541
+ 777 , // 48 - 31 - 3739
+ 769 , // 49 - 32 - 3708
+ 810 , // 50 - 33 - 3953
+ 825 , // 51 - 34 - 4020
+ 862 , // 52 - 35 - 4277
+ 4 , // 53 - 36 - 17
+ 425 , // 54 - 37 - 2062
+ 297 , // 55 - 38 - 1439
+ 379 , // 56 - 39 - 1832
+ 543 , // 57 - 3a - 2598
+ 670 , // 58 - 3b - 3194
+ 352 , // 59 - 3c - 1705
+ 831 , // 60 - 3d - 4045
+ 539 , // 61 - 3e - 2581
+ 440 , // 62 - 3f - 2133
+ 476 , // 63 - 40 - 2306
+ 738 , // 64 - 41 - 3570
+ 767 , // 65 - 42 - 3701
+ 798 , // 66 - 43 - 3859
+ 779 , // 67 - 44 - 3746
+ 71 , // 68 - 45 - 336
+ 599 , // 69 - 46 - 2830
+ 364 , // 70 - 47 - 1754
+ 594 , // 71 - 48 - 2811
+ 747 , // 72 - 49 - 3610
+ 752 , // 73 - 4a - 3632
+ 450 , // 74 - 4b - 2172
+ 525 , // 75 - 4c - 2508
+ 43 , // 76 - 4d - 199
+ 537 , // 77 - 4e - 2574
+ 657 , // 78 - 4f - 3124
+ 527 , // 79 - 50 - 2515
+ 74 , // 80 - 51 - 348
+ 107 , // 81 - 52 - 516
+ 448 , // 82 - 53 - 2165
+ 493 , // 83 - 54 - 2375
+ 547 , // 84 - 55 - 2614
+ 356 , // 85 - 56 - 1719
+ 455 , // 86 - 57 - 2191
+ 533 , // 87 - 58 - 2556
+ 665 , // 88 - 59 - 3158
+ 745 , // 89 - 5a - 3601
+ 685 , // 90 - 5b - 3277
+ 98 , // 91 - 5c - 473
+ 408 , // 92 - 5d - 1953
+ 11 , // 93 - 5e - 45
+ 783 , // 94 - 5f - 3762
+ 459 , // 95 - 60 - 2207
+ 561 , // 96 - 61 - 2673
+ 350 , // 97 - 62 - 1698
+ 611 , // 98 - 63 - 2890
+ 295 , // 99 - 64 - 1430
+ 129 , // 100 - 65 - 620
+ 66 , // 101 - 66 - 308
+ 286 , // 102 - 67 - 1384
+ 370 , // 103 - 68 - 1777
+ 394 , // 104 - 69 - 1899
+ 833 , // 105 - 6a - 4053
+ 633 , // 106 - 6b - 3020
+ 583 , // 107 - 6c - 2765
+ 54 , // 108 - 6d - 260
+ 482 , // 109 - 6e - 2330
+ 444 , // 110 - 6f - 2149
+ 398 , // 111 - 70 - 1915
+ 457 , // 112 - 71 - 2200
+ 591 , // 113 - 72 - 2799
+ 762 , // 114 - 73 - 3680
+ 358 , // 115 - 74 - 1726
+ 375 , // 116 - 75 - 1816
+ 478 , // 117 - 76 - 2313
+ 704 , // 118 - 77 - 3365
+ 400 , // 119 - 78 - 1922
+ 603 , // 120 - 79 - 2854
+ 41 , // 121 - 7a - 190
+ 535 , // 122 - 7c - 2565
+ 77 , // 123 - 7e - 360
+ 791 , // 124 - 80 - 3833
+ 521 , // 125 - 81 - 2494
+ 589 , // 126 - 82 - 2792
+ 101 , // 127 - 83 - 495
+ 360 , // 128 - 84 - 1733
+ 659 , // 129 - 85 - 3131
+ 630 , // 130 - 86 - 2998
+ 653 , // 131 - 87 - 3108
+ 822 , // 132 - 88 - 4002
+ 609 , // 133 - 8c - 2881
+ 354 , // 134 - 91 - 1712
+ 470 , // 135 - 92 - 2270
+ 33 , // 136 - 401 - 150
+ 65 , // 137 - 402 - 303
+ 90 , // 138 - 403 - 428
+ 859 , // 139 - 404 - 4248
+ 104 , // 140 - 405 - 504
+ 110 , // 141 - 406 - 525
+ 118 , // 142 - 407 - 561
+ 142 , // 143 - 408 - 671
+ 240 , // 144 - 409 - 1161
+ 263 , // 145 - 40a - 1272
+ 294 , // 146 - 40b - 1425
+ 316 , // 147 - 40c - 1529
+ 378 , // 148 - 40d - 1827
+ 387 , // 149 - 40e - 1862
+ 403 , // 150 - 40f - 1931
+ 406 , // 151 - 410 - 1943
+ 414 , // 152 - 411 - 1991
+ 454 , // 153 - 412 - 2186
+ 569 , // 154 - 413 - 2707
+ 554 , // 155 - 414 - 2641
+ 606 , // 156 - 415 - 2866
+ 615 , // 157 - 416 - 2904
+ 638 , // 158 - 417 - 3043
+ 643 , // 159 - 418 - 3062
+ 651 , // 160 - 419 - 3098
+ 383 , // 161 - 41a - 1846
+ 688 , // 162 - 41b - 3286
+ 710 , // 163 - 41c - 3389
+ 737 , // 164 - 41d - 3565
+ 761 , // 165 - 41e - 3675
+ 776 , // 166 - 41f - 3734
+ 797 , // 167 - 420 - 3854
+ 397 , // 168 - 421 - 1910
+ 794 , // 169 - 422 - 3842
+ 59 , // 170 - 423 - 278
+ 690 , // 171 - 424 - 3293
+ 279 , // 172 - 425 - 1356
+ 507 , // 173 - 426 - 2431
+ 499 , // 174 - 427 - 2399
+ 759 , // 175 - 428 - 3663
+ 285 , // 176 - 429 - 1379
+ 813 , // 177 - 42a - 3962
+ 390 , // 178 - 42b - 1881
+ 53 , // 179 - 42c - 250
+ 281 , // 180 - 42d - 1363
+ 385 , // 181 - 42e - 1854
+ 524 , // 182 - 42f - 2503
+ 733 , // 183 - 430 - 3548
+ 778 , // 184 - 431 - 3741
+ 771 , // 185 - 432 - 3715
+ 811 , // 186 - 433 - 3955
+ 826 , // 187 - 434 - 4022
+ 863 , // 188 - 435 - 4279
+ 6 , // 189 - 436 - 24
+ 426 , // 190 - 437 - 2064
+ 299 , // 191 - 438 - 1446
+ 380 , // 192 - 439 - 1834
+ 544 , // 193 - 43a - 2600
+ 672 , // 194 - 43b - 3201
+ 832 , // 195 - 43d - 4047
+ 541 , // 196 - 43e - 2588
+ 441 , // 197 - 43f - 2135
+ 477 , // 198 - 440 - 2308
+ 740 , // 199 - 441 - 3577
+ 768 , // 200 - 442 - 3703
+ 804 , // 201 - 443 - 3902
+ 780 , // 202 - 444 - 3748
+ 73 , // 203 - 445 - 343
+ 602 , // 204 - 446 - 2849
+ 365 , // 205 - 447 - 1756
+ 595 , // 206 - 448 - 2813
+ 748 , // 207 - 449 - 3612
+ 753 , // 208 - 44a - 3634
+ 451 , // 209 - 44b - 2174
+ 526 , // 210 - 44c - 2510
+ 44 , // 211 - 44d - 201
+ 538 , // 212 - 44e - 2576
+ 658 , // 213 - 44f - 3126
+ 529 , // 214 - 450 - 2524
+ 75 , // 215 - 451 - 350
+ 108 , // 216 - 452 - 518
+ 449 , // 217 - 453 - 2167
+ 494 , // 218 - 454 - 2377
+ 548 , // 219 - 455 - 2616
+ 357 , // 220 - 456 - 1721
+ 456 , // 221 - 457 - 2194
+ 534 , // 222 - 458 - 2559
+ 669 , // 223 - 459 - 3184
+ 746 , // 224 - 45a - 3604
+ 686 , // 225 - 45b - 3279
+ 100 , // 226 - 45c - 484
+ 410 , // 227 - 45d - 1962
+ 12 , // 228 - 45e - 47
+ 785 , // 229 - 45f - 3773
+ 460 , // 230 - 460 - 2209
+ 563 , // 231 - 461 - 2680
+ 351 , // 232 - 462 - 1700
+ 612 , // 233 - 463 - 2892
+ 296 , // 234 - 464 - 1433
+ 130 , // 235 - 465 - 622
+ 67 , // 236 - 466 - 311
+ 292 , // 237 - 467 - 1418
+ 374 , // 238 - 468 - 1806
+ 395 , // 239 - 469 - 1902
+ 835 , // 240 - 46a - 4060
+ 634 , // 241 - 46b - 3023
+ 584 , // 242 - 46c - 2768
+ 55 , // 243 - 46d - 262
+ 483 , // 244 - 46e - 2332
+ 445 , // 245 - 46f - 2151
+ 399 , // 246 - 470 - 1917
+ 458 , // 247 - 471 - 2202
+ 592 , // 248 - 472 - 2801
+ 764 , // 249 - 473 - 3687
+ 359 , // 250 - 474 - 1728
+ 376 , // 251 - 475 - 1819
+ 479 , // 252 - 476 - 2315
+ 708 , // 253 - 477 - 3382
+ 401 , // 254 - 478 - 1924
+ 604 , // 255 - 479 - 2857
+ 42 , // 256 - 47a - 193
+ 536 , // 257 - 47c - 2568
+ 78 , // 258 - 47e - 362
+ 792 , // 259 - 480 - 3835
+ 522 , // 260 - 481 - 2496
+ 590 , // 261 - 482 - 2794
+ 102 , // 262 - 483 - 497
+ 362 , // 263 - 484 - 1742
+ 660 , // 264 - 485 - 3134
+ 632 , // 265 - 486 - 3009
+ 654 , // 266 - 487 - 3110
+ 823 , // 267 - 488 - 4004
+ 610 , // 268 - 48c - 2884
+ 355 , // 269 - 491 - 1714
+ 472 , // 270 - 492 - 2279
+ 627 , // 271 - 501 - 2972
+ 628 , // 272 - 5fe - 2980
+ 22 , // 273 - 801 - 95
+ 91 , // 274 - 803 - 433
+ 844 , // 275 - 804 - 4110
+ 117 , // 276 - 807 - 556
+ 174 , // 277 - 809 - 831
+ 267 , // 278 - 80a - 1299
+ 302 , // 279 - 80c - 1459
+ 405 , // 280 - 810 - 1938
+ 566 , // 281 - 813 - 2692
+ 575 , // 282 - 814 - 2733
+ 623 , // 283 - 816 - 2944
+ 642 , // 284 - 818 - 3057
+ 650 , // 285 - 819 - 3093
+ 722 , // 286 - 81a - 3480
+ 736 , // 287 - 81d - 3560
+ 796 , // 288 - 820 - 3849
+ 51 , // 289 - 82c - 233
+ 126 , // 290 - 82e - 605
+ 770 , // 291 - 832 - 3710
+ 673 , // 292 - 83b - 3206
+ 353 , // 293 - 83c - 1707
+ 540 , // 294 - 83e - 2583
+ 802 , // 295 - 843 - 3885
+ 72 , // 296 - 845 - 338
+ 601 , // 297 - 846 - 2839
+ 749 , // 298 - 849 - 3617
+ 531 , // 299 - 850 - 2536
+ 667 , // 300 - 859 - 3167
+ 412 , // 301 - 85d - 1979
+ 787 , // 302 - 85f - 3792
+ 463 , // 303 - 860 - 2233
+ 562 , // 304 - 861 - 2675
+ 290 , // 305 - 867 - 1403
+ 635 , // 306 - 86b - 3029
+ 763 , // 307 - 873 - 3682
+ 626 , // 308 - 901 - 2959
+ 629 , // 309 - 9ff - 2989
+ 19 , // 310 - c01 - 80
+ 851 , // 311 - c04 - 4173
+ 115 , // 312 - c07 - 546
+ 151 , // 313 - c09 - 716
+ 262 , // 314 - c0a - 1267
+ 307 , // 315 - c0c - 1484
+ 716 , // 316 - c1a - 3423
+ 671 , // 317 - c3b - 3196
+ 532 , // 318 - c50 - 2546
+ 134 , // 319 - c51 - 638
+ 636 , // 320 - c6b - 3035
+ 27 , // 321 - 1001 - 120
+ 856 , // 322 - 1004 - 4219
+ 122 , // 323 - 1007 - 588
+ 159 , // 324 - 1009 - 756
+ 265 , // 325 - 100a - 1289
+ 311 , // 326 - 100c - 1504
+ 382 , // 327 - 101a - 1841
+ 695 , // 328 - 103b - 3316
+ 790 , // 329 - 105f - 3822
+ 18 , // 330 - 1401 - 75
+ 853 , // 331 - 1404 - 4190
+ 121 , // 332 - 1407 - 583
+ 213 , // 333 - 1409 - 1026
+ 258 , // 334 - 140a - 1247
+ 324 , // 335 - 140c - 1569
+ 85 , // 336 - 141a - 402
+ 696 , // 337 - 143b - 3322
+ 28 , // 338 - 1801 - 125
+ 184 , // 339 - 1809 - 881
+ 269 , // 340 - 180a - 1309
+ 326 , // 341 - 180c - 1579
+ 721 , // 342 - 181a - 3470
+ 692 , // 343 - 183b - 3301
+ 39 , // 344 - 1c01 - 180
+ 246 , // 345 - 1c09 - 1191
+ 260 , // 346 - 1c0a - 1257
+ 301 , // 347 - 1c0c - 1453
+ 715 , // 348 - 1c1a - 3413
+ 693 , // 349 - 1c3b - 3307
+ 30 , // 350 - 2001 - 135
+ 190 , // 351 - 2009 - 911
+ 277 , // 352 - 200a - 1349
+ 337 , // 353 - 200c - 1634
+ 83 , // 354 - 201a - 385
+ 700 , // 355 - 203b - 3340
+ 40 , // 356 - 2401 - 185
+ 145 , // 357 - 2409 - 684
+ 257 , // 358 - 240a - 1242
+ 308 , // 359 - 240c - 1489
+ 724 , // 360 - 241a - 3500
+ 698 , // 361 - 243b - 3331
+ 37 , // 362 - 2801 - 170
+ 158 , // 363 - 2809 - 751
+ 270 , // 364 - 280a - 1314
+ 340 , // 365 - 280c - 1649
+ 718 , // 366 - 281a - 3443
+ 23 , // 367 - 2c01 - 100
+ 235 , // 368 - 2c09 - 1136
+ 253 , // 369 - 2c0a - 1222
+ 313 , // 370 - 2c0c - 1514
+ 723 , // 371 - 2c1a - 3490
+ 26 , // 372 - 3001 - 115
+ 248 , // 373 - 3009 - 1201
+ 261 , // 374 - 300a - 1262
+ 312 , // 375 - 300c - 1509
+ 717 , // 376 - 301a - 3433
+ 25 , // 377 - 3401 - 110
+ 215 , // 378 - 3409 - 1036
+ 256 , // 379 - 340a - 1237
+ 329 , // 380 - 340c - 1594
+ 15 , // 381 - 3801 - 60
+ 183 , // 382 - 3809 - 876
+ 276 , // 383 - 380a - 1344
+ 325 , // 384 - 380c - 1574
+ 16 , // 385 - 3c01 - 65
+ 182 , // 386 - 3c09 - 871
+ 273 , // 387 - 3c0a - 1329
+ 322 , // 388 - 3c0c - 1559
+ 32 , // 389 - 4001 - 145
+ 187 , // 390 - 4009 - 896
+ 254 , // 391 - 400a - 1227
+ 206 , // 392 - 4409 - 991
+ 274 , // 393 - 440a - 1334
+ 225 , // 394 - 4809 - 1086
+ 266 , // 395 - 480a - 1294
+ 268 , // 396 - 4c0a - 1304
+ 272 , // 397 - 500a - 1324
+ 275 , // 398 - 540a - 1339
+ 252 , // 399 - 580a - 1216
+ 259 , // 400 - 5c0a - 1252
+ 82 , // 401 - 641a - 378
+ 84 , // 402 - 681a - 395
+ 714 , // 403 - 6c1a - 3406
+ 720 , // 404 - 701a - 3463
+ 697 , // 405 - 703b - 3328
+ 50 , // 406 - 742c - 226
+ 699 , // 407 - 743b - 3337
+ 841 , // 408 - 7804 - 4096
+ 574 , // 409 - 7814 - 2731
+ 81 , // 410 - 781a - 376
+ 52 , // 411 - 782c - 243
+ 691 , // 412 - 783b - 3298
+ 801 , // 413 - 7843 - 3878
+ 528 , // 414 - 7850 - 2517
+ 409 , // 415 - 785d - 1955
+ 789 , // 416 - 785f - 3814
+ 850 , // 417 - 7c04 - 4166
+ 553 , // 418 - 7c14 - 2639
+ 713 , // 419 - 7c1a - 3404
+ 758 , // 420 - 7c28 - 3656
+ 125 , // 421 - 7c2e - 602
+ 694 , // 422 - 7c3b - 3313
+ 803 , // 423 - 7c43 - 3895
+ 600 , // 424 - 7c46 - 2832
+ 530 , // 425 - 7c50 - 2529
+ 666 , // 426 - 7c59 - 3160
+ 99 , // 427 - 7c5c - 476
+ 411 , // 428 - 7c5d - 1972
+ 786 , // 429 - 7c5f - 3784
+ 289 , // 430 - 7c67 - 1396
+ 371 , // 431 - 7c68 - 1779
+ 631 , // 432 - 7c86 - 3001
+ 471 , // 433 - 7c92 - 2272
+ 824 , // 434 - 1007f - 4009
+ 119 , // 435 - 10407 - 566
+ 388 , // 436 - 1040e - 1867
+ 427 , // 437 - 10437 - 2069
+ 846 , // 438 - 20804 - 4127
+ 858 , // 439 - 21004 - 4236
+ 855 , // 440 - 21404 - 4207
+ 860 , // 441 - 30404 - 4253
+ 861 , // 442 - 40404 - 4265
+ 415 , // 443 - 40411 - 1996
+ 852 , // 444 - 40c04 - 4178
+ 854 , // 445 - 41404 - 4195
+ 845 , // 446 - 50804 - 4115
+ 857 // 447 - 51004 - 4224
+ };
+
+ internal static string LCIDToLocaleName(int culture)
+ {
+ int left = 0;
+ int right = s_lcids.Length - 1;
+ int index;
+
+ Debug.Assert(s_lcids.Length == s_lcidToCultureNameIndices.Length);
+
+ while (left <= right)
+ {
+ index = (right + left) / 2;
+
+ if (culture == s_lcids[index])
+ {
+ int indexToLocaleNamesIndices = s_lcidToCultureNameIndices[index];
+ Debug.Assert(indexToLocaleNamesIndices < s_localeNamesIndices.Length - 1);
+
+ return c_localeNames.Substring(s_localeNamesIndices[indexToLocaleNamesIndices],
+ s_localeNamesIndices[indexToLocaleNamesIndices + 1] -
+ s_localeNamesIndices[indexToLocaleNamesIndices]);
+ }
+ else if (culture < s_lcids[index])
+ {
+ right = index - 1;
+ }
+ else
+ {
+ left = index + 1;
+ }
+ }
+
+ return null;
+ }
+
+ internal static int GetLocaleDataNumericPart(string cultureName, LocaleDataParts part)
+ {
+ int index = SearchCultureName(cultureName);
+ if (index < 0)
+ {
+ return -1;
+ }
+
+ Debug.Assert((s_localeNamesIndices.Length-1 == (s_nameIndexToNumericData.Length/NUMERIC_LOCALE_DATA_COUNT_PER_ROW)) &&
+ index < s_localeNamesIndices.Length);
+
+ return s_nameIndexToNumericData[index * NUMERIC_LOCALE_DATA_COUNT_PER_ROW + (int) part];
+ }
+
+ internal static string GetThreeLetterWindowsLangageName(string cultureName)
+ {
+ int index = SearchCultureName(cultureName);
+ if (index < 0)
+ {
+ return null;
+ }
+
+ Debug.Assert(s_localeNamesIndices.Length-1 == (c_threeLetterWindowsLanguageName.Length / 3));
+ return c_threeLetterWindowsLanguageName.Substring(index * 3, 3);
+ }
+
+ internal static string GetLocaleDataMappedCulture(string cultureName, LocaleDataParts part)
+ {
+ int indexToIndicesTable = GetLocaleDataNumericPart(cultureName, part);
+ if (indexToIndicesTable < 0)
+ {
+ return ""; // fallback to invariant
+ }
+
+ Debug.Assert(indexToIndicesTable < s_localeNamesIndices.Length-1);
+
+ return c_localeNames.Substring(s_localeNamesIndices[indexToIndicesTable],
+ s_localeNamesIndices[indexToIndicesTable+1] - s_localeNamesIndices[indexToIndicesTable]);
+ }
+
+ internal static string GetSpecificCultureName(string cultureName)
+ {
+ return GetLocaleDataMappedCulture(cultureName, LocaleDataParts.SpecificLocaleIndex);
+ }
+
+ internal static string GetConsoleUICulture(string cultureName)
+ {
+ return GetLocaleDataMappedCulture(cultureName, LocaleDataParts.ConsoleLocaleIndex);
+ }
+
+ // SearchCultureName will binary search c_localeNames using s_localeNamesIndices.
+ // return index in s_localeNamesIndices, or -1 if it fail finding any match
+ private static int SearchCultureName(string name)
+ {
+ int left = 0;
+ int right = s_localeNamesIndices.Length - 2;
+ int index;
+ int result;
+
+ Debug.Assert(s_localeNamesIndices[s_localeNamesIndices.Length - 1] == c_localeNames.Length);
+
+ name = CultureData.AnsiToLower(name);
+
+ // Binary search the array until we have only a couple of elements left and then
+ // just walk those elements.
+ while ((right - left) > 3)
+ {
+ index = ((right - left) / 2) + left;
+
+ Debug.Assert(index < s_localeNamesIndices.Length - 1);
+ result = CompareOrdinal(name, c_localeNames, s_localeNamesIndices[index], s_localeNamesIndices[index + 1] - s_localeNamesIndices[index]);
+ if (result == 0)
+ {
+ return index;
+ }
+ else if (result < 0)
+ {
+ right = index;
+ }
+ else
+ {
+ left = index;
+ }
+ }
+
+ // Walk the remaining elements (it'll be 3 or fewer).
+ for (; left <= right; left++)
+ {
+ Debug.Assert(left < s_localeNamesIndices.Length - 1);
+ if (CompareOrdinal(name, c_localeNames, s_localeNamesIndices[left], s_localeNamesIndices[left + 1] - s_localeNamesIndices[left]) == 0)
+ {
+ return (left);
+ }
+ }
+
+ // couldn't find culture name
+ return -1;
+ }
+
+ // optimized to avoid parameters checking
+ private static int CompareOrdinal(string s1, string s2, int index, int length)
+ {
+ int count = s1.Length;
+ if (count > length)
+ count = length;
+
+ int i = 0;
+ while (i < count && s1[i] == s2[index + i])
+ i++;
+
+ if (i < count)
+ return (int)(s1[i] - s2[index + i]);
+
+ return s1.Length - length;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/NumberStyles.cs b/src/mscorlib/shared/System/Globalization/NumberStyles.cs
new file mode 100644
index 0000000000..5909d65a2c
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/NumberStyles.cs
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Contains valid formats for Numbers recognized by
+** the Number class' parsing code.
+**
+**
+===========================================================*/
+
+namespace System.Globalization
+{
+ [Flags]
+ public enum NumberStyles
+ {
+ // Bit flag indicating that leading whitespace is allowed. Character values
+ // 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x0020 are considered to be
+ // whitespace.
+
+ None = 0x00000000,
+
+ AllowLeadingWhite = 0x00000001,
+
+ AllowTrailingWhite = 0x00000002, //Bitflag indicating trailing whitespace is allowed.
+
+ AllowLeadingSign = 0x00000004, //Can the number start with a sign char.
+ //Specified by NumberFormatInfo.PositiveSign and NumberFormatInfo.NegativeSign
+
+ AllowTrailingSign = 0x00000008, //Allow the number to end with a sign char
+
+ AllowParentheses = 0x00000010, //Allow the number to be enclosed in parens
+
+ AllowDecimalPoint = 0x00000020, //Allow a decimal point
+
+ AllowThousands = 0x00000040, //Allow thousands separators (more properly, allow group separators)
+
+ AllowExponent = 0x00000080, //Allow an exponent
+
+ AllowCurrencySymbol = 0x00000100, //Allow a currency symbol.
+
+ AllowHexSpecifier = 0x00000200, //Allow specifiying hexadecimal.
+ //Common uses. These represent some of the most common combinations of these flags.
+
+
+ Integer = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign,
+
+ HexNumber = AllowLeadingWhite | AllowTrailingWhite | AllowHexSpecifier,
+
+ Number = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign |
+ AllowDecimalPoint | AllowThousands,
+
+ Float = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign |
+ AllowDecimalPoint | AllowExponent,
+
+ Currency = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign |
+ AllowParentheses | AllowDecimalPoint | AllowThousands | AllowCurrencySymbol,
+
+ Any = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign | AllowTrailingSign |
+ AllowParentheses | AllowDecimalPoint | AllowThousands | AllowCurrencySymbol | AllowExponent,
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/PersianCalendar.cs b/src/mscorlib/shared/System/Globalization/PersianCalendar.cs
new file mode 100644
index 0000000000..445bbd6d0c
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/PersianCalendar.cs
@@ -0,0 +1,606 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ // Modern Persian calendar is a solar observation based calendar. Each new year begins on the day when the vernal equinox occurs before noon.
+ // The epoch is the date of the vernal equinox prior to the epoch of the Islamic calendar (March 19, 622 Julian or March 22, 622 Gregorian)
+
+ // There is no Persian year 0. Ordinary years have 365 days. Leap years have 366 days with the last month (Esfand) gaining the extra day.
+ /*
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 0622/03/22 9999/12/31
+ ** Persian 0001/01/01 9378/10/13
+ */
+
+ [Serializable]
+ public class PersianCalendar : Calendar
+ {
+ public static readonly int PersianEra = 1;
+
+ internal static long PersianEpoch = new DateTime(622, 3, 22).Ticks / GregorianCalendar.TicksPerDay;
+ private const int ApproximateHalfYear = 180;
+
+ internal const int DatePartYear = 0;
+ internal const int DatePartDayOfYear = 1;
+ internal const int DatePartMonth = 2;
+ internal const int DatePartDay = 3;
+ internal const int MonthsPerYear = 12;
+
+ internal static int[] DaysToMonth = { 0, 31, 62, 93, 124, 155, 186, 216, 246, 276, 306, 336, 366 };
+
+ internal const int MaxCalendarYear = 9378;
+ internal const int MaxCalendarMonth = 10;
+ internal const int MaxCalendarDay = 13;
+
+ // Persian calendar (year: 1, month: 1, day:1 ) = Gregorian (year: 622, month: 3, day: 22)
+ // This is the minimal Gregorian date that we support in the PersianCalendar.
+ internal static DateTime minDate = new DateTime(622, 3, 22);
+ internal static DateTime maxDate = DateTime.MaxValue;
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.SolarCalendar;
+ }
+ }
+
+ // Construct an instance of Persian calendar.
+
+ public PersianCalendar()
+ {
+ }
+
+
+ internal override CalendarId BaseCalendarID
+ {
+ get
+ {
+ return CalendarId.GREGORIAN;
+ }
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return CalendarId.PERSIAN;
+ }
+ }
+
+
+ /*=================================GetAbsoluteDatePersian==========================
+ **Action: Gets the Absolute date for the given Persian date. The absolute date means
+ ** the number of days from January 1st, 1 A.D.
+ **Returns:
+ **Arguments:
+ **Exceptions:
+ ============================================================================*/
+
+ private long GetAbsoluteDatePersian(int year, int month, int day)
+ {
+ if (year >= 1 && year <= MaxCalendarYear && month >= 1 && month <= 12)
+ {
+ int ordinalDay = DaysInPreviousMonths(month) + day - 1; // day is one based, make 0 based since this will be the number of days we add to beginning of year below
+ int approximateDaysFromEpochForYearStart = (int)(CalendricalCalculationsHelper.MeanTropicalYearInDays * (year - 1));
+ long yearStart = CalendricalCalculationsHelper.PersianNewYearOnOrBefore(PersianEpoch + approximateDaysFromEpochForYearStart + ApproximateHalfYear);
+ yearStart += ordinalDay;
+ return yearStart;
+ }
+ throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
+ }
+
+ internal static void CheckTicksRange(long ticks)
+ {
+ if (ticks < minDate.Ticks || ticks > maxDate.Ticks)
+ {
+ throw new ArgumentOutOfRangeException(
+ "time",
+ String.Format(
+ CultureInfo.InvariantCulture,
+ SR.ArgumentOutOfRange_CalendarRange,
+ minDate,
+ maxDate));
+ }
+ }
+
+ internal static void CheckEraRange(int era)
+ {
+ if (era != CurrentEra && era != PersianEra)
+ {
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+ }
+
+ internal static void CheckYearRange(int year, int era)
+ {
+ CheckEraRange(era);
+ if (year < 1 || year > MaxCalendarYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ MaxCalendarYear));
+ }
+ }
+
+ internal static void CheckYearMonthRange(int year, int month, int era)
+ {
+ CheckYearRange(year, era);
+ if (year == MaxCalendarYear)
+ {
+ if (month > MaxCalendarMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(month),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ MaxCalendarMonth));
+ }
+ }
+
+ if (month < 1 || month > 12)
+ {
+ throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
+ }
+ }
+
+ private static int MonthFromOrdinalDay(int ordinalDay)
+ {
+ Debug.Assert(ordinalDay <= 366);
+ int index = 0;
+ while (ordinalDay > DaysToMonth[index])
+ index++;
+
+ return index;
+ }
+
+ private static int DaysInPreviousMonths(int month)
+ {
+ Debug.Assert(1 <= month && month <= 12);
+ --month; // months are one based but for calculations use 0 based
+ return DaysToMonth[month];
+ }
+
+ /*=================================GetDatePart==========================
+ **Action: Returns a given date part of this <i>DateTime</i>. This method is used
+ ** to compute the year, day-of-year, month, or day part.
+ **Returns:
+ **Arguments:
+ **Exceptions: ArgumentException if part is incorrect.
+ ============================================================================*/
+
+ internal int GetDatePart(long ticks, int part)
+ {
+ long NumDays; // The calculation buffer in number of days.
+
+ CheckTicksRange(ticks);
+
+ //
+ // Get the absolute date. The absolute date is the number of days from January 1st, 1 A.D.
+ // 1/1/0001 is absolute date 1.
+ //
+ NumDays = ticks / GregorianCalendar.TicksPerDay + 1;
+
+ //
+ // Calculate the appromixate Persian Year.
+ //
+
+ long yearStart = CalendricalCalculationsHelper.PersianNewYearOnOrBefore(NumDays);
+ int y = (int)(Math.Floor(((yearStart - PersianEpoch) / CalendricalCalculationsHelper.MeanTropicalYearInDays) + 0.5)) + 1;
+ Debug.Assert(y >= 1);
+
+ if (part == DatePartYear)
+ {
+ return y;
+ }
+
+ //
+ // Calculate the Persian Month.
+ //
+
+ int ordinalDay = (int)(NumDays - CalendricalCalculationsHelper.GetNumberOfDays(this.ToDateTime(y, 1, 1, 0, 0, 0, 0, 1)));
+
+ if (part == DatePartDayOfYear)
+ {
+ return ordinalDay;
+ }
+
+ int m = MonthFromOrdinalDay(ordinalDay);
+ Debug.Assert(ordinalDay >= 1);
+ Debug.Assert(m >= 1 && m <= 12);
+ if (part == DatePartMonth)
+ {
+ return m;
+ }
+
+ int d = ordinalDay - DaysInPreviousMonths(m);
+ Debug.Assert(1 <= d);
+ Debug.Assert(d <= 31);
+
+ //
+ // Calculate the Persian Day.
+ //
+
+ if (part == DatePartDay)
+ {
+ return (d);
+ }
+
+ // Incorrect part value.
+ throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing);
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // months to the specified DateTime. The result is computed by incrementing
+ // (or decrementing) the year and month parts of the specified DateTime by
+ // value months, and, if required, adjusting the day part of the
+ // resulting date downwards to the last day of the resulting month in the
+ // resulting year. The time-of-day part of the result is the same as the
+ // time-of-day part of the specified DateTime.
+ //
+ // In more precise terms, considering the specified DateTime to be of the
+ // form y / m / d + t, where y is the
+ // year, m is the month, d is the day, and t is the
+ // time-of-day, the result is y1 / m1 / d1 + t,
+ // where y1 and m1 are computed by adding value months
+ // to y and m, and d1 is the largest value less than
+ // or equal to d that denotes a valid day in month m1 of year
+ // y1.
+ //
+
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ if (months < -120000 || months > 120000)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(months),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ -120000,
+ 120000));
+ }
+ Contract.EndContractBlock();
+ // Get the date in Persian calendar.
+ int y = GetDatePart(time.Ticks, DatePartYear);
+ int m = GetDatePart(time.Ticks, DatePartMonth);
+ int d = GetDatePart(time.Ticks, DatePartDay);
+ int i = m - 1 + months;
+ if (i >= 0)
+ {
+ m = i % 12 + 1;
+ y = y + i / 12;
+ }
+ else
+ {
+ m = 12 + (i + 1) % 12;
+ y = y + (i - 11) / 12;
+ }
+ int days = GetDaysInMonth(y, m);
+ if (d > days)
+ {
+ d = days;
+ }
+ long ticks = GetAbsoluteDatePersian(y, m, d) * TicksPerDay + time.Ticks % TicksPerDay;
+ Calendar.CheckAddResult(ticks, MinSupportedDateTime, MaxSupportedDateTime);
+ return (new DateTime(ticks));
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // years to the specified DateTime. The result is computed by incrementing
+ // (or decrementing) the year part of the specified DateTime by value
+ // years. If the month and day of the specified DateTime is 2/29, and if the
+ // resulting year is not a leap year, the month and day of the resulting
+ // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
+ // parts of the result are the same as those of the specified DateTime.
+ //
+
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ return (AddMonths(time, years * 12));
+ }
+
+ // Returns the day-of-month part of the specified DateTime. The returned
+ // value is an integer between 1 and 31.
+ //
+
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartDay));
+ }
+
+ // Returns the day-of-week part of the specified DateTime. The returned value
+ // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
+ // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
+ // Thursday, 5 indicates Friday, and 6 indicates Saturday.
+ //
+
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
+ }
+
+ // Returns the day-of-year part of the specified DateTime. The returned value
+ // is an integer between 1 and 366.
+ //
+
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartDayOfYear));
+ }
+
+ // Returns the number of days in the month given by the year and
+ // month arguments.
+ //
+
+
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ CheckYearMonthRange(year, month, era);
+
+ if ((month == MaxCalendarMonth) && (year == MaxCalendarYear))
+ {
+ return MaxCalendarDay;
+ }
+
+ int daysInMonth = DaysToMonth[month] - DaysToMonth[month - 1];
+ if ((month == MonthsPerYear) && !IsLeapYear(year))
+ {
+ Debug.Assert(daysInMonth == 30);
+ --daysInMonth;
+ }
+ return daysInMonth;
+ }
+
+ // Returns the number of days in the year given by the year argument for the current era.
+ //
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ CheckYearRange(year, era);
+ if (year == MaxCalendarYear)
+ {
+ return DaysToMonth[MaxCalendarMonth - 1] + MaxCalendarDay;
+ }
+ // Common years have 365 days. Leap years have 366 days.
+ return (IsLeapYear(year, CurrentEra) ? 366 : 365);
+ }
+
+ // Returns the era for the specified DateTime value.
+
+
+ public override int GetEra(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+ return (PersianEra);
+ }
+
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (new int[] { PersianEra });
+ }
+ }
+
+ // Returns the month part of the specified DateTime. The returned value is an
+ // integer between 1 and 12.
+ //
+
+
+ public override int GetMonth(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartMonth));
+ }
+
+ // Returns the number of months in the specified year and era.
+
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ CheckYearRange(year, era);
+ if (year == MaxCalendarYear)
+ {
+ return MaxCalendarMonth;
+ }
+ return (12);
+ }
+
+ // Returns the year part of the specified DateTime. The returned value is an
+ // integer between 1 and MaxCalendarYear.
+ //
+
+
+ public override int GetYear(DateTime time)
+ {
+ return (GetDatePart(time.Ticks, DatePartYear));
+ }
+
+ // Checks whether a given day in the specified era is a leap day. This method returns true if
+ // the date is a leap day, or false if not.
+ //
+
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ // The year/month/era value checking is done in GetDaysInMonth().
+ int daysInMonth = GetDaysInMonth(year, month, era);
+ if (day < 1 || day > daysInMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Day,
+ daysInMonth,
+ month));
+ }
+ return (IsLeapYear(year, era) && month == 12 && day == 30);
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this calendar does not have leap month, or this year is not a leap year.
+ //
+
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ CheckYearRange(year, era);
+ return (0);
+ }
+
+ // Checks whether a given month in the specified era is a leap month. This method returns true if
+ // month is a leap month, or false if not.
+ //
+
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ CheckYearMonthRange(year, month, era);
+ return (false);
+ }
+
+ // Checks whether a given year in the specified era is a leap year. This method returns true if
+ // year is a leap year, or false if not.
+ //
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ CheckYearRange(year, era);
+
+ if (year == MaxCalendarYear)
+ {
+ return false;
+ }
+
+ return (GetAbsoluteDatePersian(year + 1, 1, 1) - GetAbsoluteDatePersian(year, 1, 1)) == 366;
+ }
+
+ // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
+ //
+
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ // The year/month/era checking is done in GetDaysInMonth().
+ int daysInMonth = GetDaysInMonth(year, month, era);
+ if (day < 1 || day > daysInMonth)
+ {
+ // BCLDebug.Log("year = " + year + ", month = " + month + ", day = " + day);
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Day,
+ daysInMonth,
+ month));
+ }
+
+ long lDate = GetAbsoluteDatePersian(year, month, day);
+
+ if (lDate >= 0)
+ {
+ return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond)));
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
+ }
+ }
+
+ private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1410;
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ if (twoDigitYearMax == -1)
+ {
+ twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
+ }
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ VerifyWritable();
+ if (value < 99 || value > MaxCalendarYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(value),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 99,
+ MaxCalendarYear));
+ }
+ twoDigitYearMax = value;
+ }
+ }
+
+
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.EndContractBlock();
+
+ if (year < 100)
+ {
+ return (base.ToFourDigitYear(year));
+ }
+
+ if (year > MaxCalendarYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ MaxCalendarYear));
+ }
+ return (year);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/SortVersion.cs b/src/mscorlib/shared/System/Globalization/SortVersion.cs
new file mode 100644
index 0000000000..a7aef6d84b
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/SortVersion.cs
@@ -0,0 +1,98 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ [Serializable]
+ public sealed class SortVersion : IEquatable<SortVersion>
+ {
+ private int _nlsVersion;
+ private Guid _sortId;
+
+ public int FullVersion
+ {
+ get
+ {
+ return _nlsVersion;
+ }
+ }
+
+ public Guid SortId
+ {
+ get
+ {
+ return _sortId;
+ }
+ }
+
+ public SortVersion(int fullVersion, Guid sortId)
+ {
+ _sortId = sortId;
+ _nlsVersion = fullVersion;
+ }
+
+ internal SortVersion(int nlsVersion, int effectiveId, Guid customVersion)
+ {
+ _nlsVersion = nlsVersion;
+
+ if (customVersion == Guid.Empty)
+ {
+ byte b1 = (byte)(effectiveId >> 24);
+ byte b2 = (byte)((effectiveId & 0x00FF0000) >> 16);
+ byte b3 = (byte)((effectiveId & 0x0000FF00) >> 8);
+ byte b4 = (byte)(effectiveId & 0xFF);
+ customVersion = new Guid(0, 0, 0, 0, 0, 0, 0, b1, b2, b3, b4);
+ }
+
+ _sortId = customVersion;
+ }
+
+ public override bool Equals(object obj)
+ {
+ SortVersion n = obj as SortVersion;
+ if (n != null)
+ {
+ return this.Equals(n);
+ }
+
+ return false;
+ }
+
+ public bool Equals(SortVersion other)
+ {
+ if (other == null)
+ {
+ return false;
+ }
+
+ return _nlsVersion == other._nlsVersion && _sortId == other._sortId;
+ }
+
+ public override int GetHashCode()
+ {
+ return _nlsVersion * 7 | _sortId.GetHashCode();
+ }
+
+ public static bool operator ==(SortVersion left, SortVersion right)
+ {
+ if (((object)left) != null)
+ {
+ return left.Equals(right);
+ }
+
+ if (((object)right) != null)
+ {
+ return right.Equals(left);
+ }
+
+ // Both null.
+ return true;
+ }
+
+ public static bool operator !=(SortVersion left, SortVersion right)
+ {
+ return !(left == right);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/TaiwanCalendar.cs b/src/mscorlib/shared/System/Globalization/TaiwanCalendar.cs
new file mode 100644
index 0000000000..2e735e0cb9
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/TaiwanCalendar.cs
@@ -0,0 +1,285 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ /*=================================TaiwanCalendar==========================
+ **
+ ** Taiwan calendar is based on the Gregorian calendar. And the year is an offset to Gregorian calendar.
+ ** That is,
+ ** Taiwan year = Gregorian year - 1911. So 1912/01/01 A.D. is Taiwan 1/01/01
+ **
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 1912/01/01 9999/12/31
+ ** Taiwan 01/01/01 8088/12/31
+ ============================================================================*/
+
+ [Serializable]
+ public class TaiwanCalendar : Calendar
+ {
+ //
+ // The era value for the current era.
+ //
+
+ // Since
+ // Gregorian Year = Era Year + yearOffset
+ // When Gregorian Year 1912 is year 1, so that
+ // 1912 = 1 + yearOffset
+ // So yearOffset = 1911
+ //m_EraInfo[0] = new EraInfo(1, new DateTime(1912, 1, 1).Ticks, 1911, 1, GregorianCalendar.MaxYear - 1911);
+
+ // Initialize our era info.
+ internal static EraInfo[] taiwanEraInfo = new EraInfo[] {
+ new EraInfo( 1, 1912, 1, 1, 1911, 1, GregorianCalendar.MaxYear - 1911) // era #, start year/month/day, yearOffset, minEraYear
+ };
+
+ internal static volatile Calendar s_defaultInstance;
+
+ internal GregorianCalendarHelper helper;
+
+ /*=================================GetDefaultInstance==========================
+ **Action: Internal method to provide a default intance of TaiwanCalendar. Used by NLS+ implementation
+ ** and other calendars.
+ **Returns:
+ **Arguments:
+ **Exceptions:
+ ============================================================================*/
+
+ internal static Calendar GetDefaultInstance()
+ {
+ if (s_defaultInstance == null)
+ {
+ s_defaultInstance = new TaiwanCalendar();
+ }
+ return (s_defaultInstance);
+ }
+
+ internal static readonly DateTime calendarMinValue = new DateTime(1912, 1, 1);
+
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (calendarMinValue);
+ }
+ }
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (DateTime.MaxValue);
+ }
+ }
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.SolarCalendar;
+ }
+ }
+
+ // Return the type of the Taiwan calendar.
+ //
+
+ public TaiwanCalendar()
+ {
+ try
+ {
+ new CultureInfo("zh-TW");
+ }
+ catch (ArgumentException e)
+ {
+ throw new TypeInitializationException(this.GetType().ToString(), e);
+ }
+ helper = new GregorianCalendarHelper(this, taiwanEraInfo);
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return CalendarId.TAIWAN;
+ }
+ }
+
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ return (helper.AddMonths(time, months));
+ }
+
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ return (helper.AddYears(time, years));
+ }
+
+
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ return (helper.GetDaysInMonth(year, month, era));
+ }
+
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ return (helper.GetDaysInYear(year, era));
+ }
+
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ return (helper.GetDayOfMonth(time));
+ }
+
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ return (helper.GetDayOfWeek(time));
+ }
+
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ return (helper.GetDayOfYear(time));
+ }
+
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ return (helper.GetMonthsInYear(year, era));
+ }
+
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
+ {
+ return (helper.GetWeekOfYear(time, rule, firstDayOfWeek));
+ }
+
+
+ public override int GetEra(DateTime time)
+ {
+ return (helper.GetEra(time));
+ }
+
+ public override int GetMonth(DateTime time)
+ {
+ return (helper.GetMonth(time));
+ }
+
+
+ public override int GetYear(DateTime time)
+ {
+ return (helper.GetYear(time));
+ }
+
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ return (helper.IsLeapDay(year, month, day, era));
+ }
+
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ return (helper.IsLeapYear(year, era));
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this calendar does not have leap month, or this year is not a leap year.
+ //
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ return (helper.GetLeapMonth(year, era));
+ }
+
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ return (helper.IsLeapMonth(year, month, era));
+ }
+
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era));
+ }
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (helper.Eras);
+ }
+ }
+
+ private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 99;
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ if (twoDigitYearMax == -1)
+ {
+ twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
+ }
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ VerifyWritable();
+ if (value < 99 || value > helper.MaxYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ "year",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 99,
+ helper.MaxYear));
+ }
+ twoDigitYearMax = value;
+ }
+ }
+
+ // For Taiwan calendar, four digit year is not used.
+ // Therefore, for any two digit number, we just return the original number.
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedPosNum);
+ }
+ Contract.EndContractBlock();
+
+ if (year > helper.MaxYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 1,
+ helper.MaxYear));
+ }
+ return (year);
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Globalization/TaiwanLunisolarCalendar.cs b/src/mscorlib/shared/System/Globalization/TaiwanLunisolarCalendar.cs
new file mode 100644
index 0000000000..8ba1f278e7
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/TaiwanLunisolarCalendar.cs
@@ -0,0 +1,325 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ /*
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 1912/02/18 2051/02/10
+ ** TaiwanLunisolar 1912/01/01 2050/13/29
+ */
+
+ [Serializable]
+ public class TaiwanLunisolarCalendar : EastAsianLunisolarCalendar
+ {
+ // Since
+ // Gregorian Year = Era Year + yearOffset
+ // When Gregorian Year 1912 is year 1, so that
+ // 1912 = 1 + yearOffset
+ // So yearOffset = 1911
+ //m_EraInfo[0] = new EraInfo(1, new DateTime(1912, 1, 1).Ticks, 1911, 1, GregorianCalendar.MaxYear - 1911);
+
+ // Initialize our era info.
+ internal static EraInfo[] taiwanLunisolarEraInfo = new EraInfo[] {
+ new EraInfo( 1, 1912, 1, 1, 1911, 1, GregorianCalendar.MaxYear - 1911) // era #, start year/month/day, yearOffset, minEraYear
+ };
+
+ internal GregorianCalendarHelper helper;
+
+ internal const int MIN_LUNISOLAR_YEAR = 1912;
+ internal const int MAX_LUNISOLAR_YEAR = 2050;
+
+ internal const int MIN_GREGORIAN_YEAR = 1912;
+ internal const int MIN_GREGORIAN_MONTH = 2;
+ internal const int MIN_GREGORIAN_DAY = 18;
+
+ internal const int MAX_GREGORIAN_YEAR = 2051;
+ internal const int MAX_GREGORIAN_MONTH = 2;
+ internal const int MAX_GREGORIAN_DAY = 10;
+
+ internal static DateTime minDate = new DateTime(MIN_GREGORIAN_YEAR, MIN_GREGORIAN_MONTH, MIN_GREGORIAN_DAY);
+ internal static DateTime maxDate = new DateTime((new DateTime(MAX_GREGORIAN_YEAR, MAX_GREGORIAN_MONTH, MAX_GREGORIAN_DAY, 23, 59, 59, 999)).Ticks + 9999);
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+
+ protected override int DaysInYearBeforeMinSupportedYear
+ {
+ get
+ {
+ // 1911 from ChineseLunisolarCalendar
+ return 384;
+ }
+ }
+
+ private static readonly int[,] s_yinfo =
+ {
+ /*Y LM Lmon Lday DaysPerMonth D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 #Days
+ 1912 */
+ { 0 , 2 , 18 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1913 */{ 0 , 2 , 6 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1914 */{ 5 , 1 , 26 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384
+1915 */{ 0 , 2 , 14 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354
+1916 */{ 0 , 2 , 3 , 54944 },/* 30 30 29 30 29 30 30 29 30 29 30 29 0 355
+1917 */{ 2 , 1 , 23 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384
+1918 */{ 0 , 2 , 11 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1919 */{ 7 , 2 , 1 , 18872 },/* 29 30 29 29 30 29 29 30 30 29 30 30 30 384
+1920 */{ 0 , 2 , 20 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1921 */{ 0 , 2 , 8 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1922 */{ 5 , 1 , 28 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1923 */{ 0 , 2 , 16 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1924 */{ 0 , 2 , 5 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+1925 */{ 4 , 1 , 24 , 44456 },/* 30 29 30 29 30 30 29 30 30 29 30 29 30 385
+1926 */{ 0 , 2 , 13 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1927 */{ 0 , 2 , 2 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355
+1928 */{ 2 , 1 , 23 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384
+1929 */{ 0 , 2 , 10 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1930 */{ 6 , 1 , 30 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 29 383
+1931 */{ 0 , 2 , 17 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1932 */{ 0 , 2 , 6 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355
+1933 */{ 5 , 1 , 26 , 27976 },/* 29 30 30 29 30 30 29 30 29 30 29 29 30 384
+1934 */{ 0 , 2 , 14 , 23248 },/* 29 30 29 30 30 29 30 29 30 30 29 30 0 355
+1935 */{ 0 , 2 , 4 , 11104 },/* 29 29 30 29 30 29 30 30 29 30 30 29 0 354
+1936 */{ 3 , 1 , 24 , 37744 },/* 30 29 29 30 29 29 30 30 29 30 30 30 29 384
+1937 */{ 0 , 2 , 11 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+1938 */{ 7 , 1 , 31 , 51560 },/* 30 30 29 29 30 29 29 30 29 30 30 29 30 384
+1939 */{ 0 , 2 , 19 , 51536 },/* 30 30 29 29 30 29 29 30 29 30 29 30 0 354
+1940 */{ 0 , 2 , 8 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+1941 */{ 6 , 1 , 27 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 29 384
+1942 */{ 0 , 2 , 15 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1943 */{ 0 , 2 , 5 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1944 */{ 4 , 1 , 25 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385
+1945 */{ 0 , 2 , 13 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+1946 */{ 0 , 2 , 2 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354
+1947 */{ 2 , 1 , 22 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384
+1948 */{ 0 , 2 , 10 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+1949 */{ 7 , 1 , 29 , 46248 },/* 30 29 30 30 29 30 29 29 30 29 30 29 30 384
+1950 */{ 0 , 2 , 17 , 27808 },/* 29 30 30 29 30 30 29 29 30 29 30 29 0 354
+1951 */{ 0 , 2 , 6 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 0 355
+1952 */{ 5 , 1 , 27 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384
+1953 */{ 0 , 2 , 14 , 19872 },/* 29 30 29 29 30 30 29 30 30 29 30 29 0 354
+1954 */{ 0 , 2 , 3 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+1955 */{ 3 , 1 , 24 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+1956 */{ 0 , 2 , 12 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+1957 */{ 8 , 1 , 31 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 29 383
+1958 */{ 0 , 2 , 18 , 59728 },/* 30 30 30 29 30 29 29 30 29 30 29 30 0 355
+1959 */{ 0 , 2 , 8 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+1960 */{ 6 , 1 , 28 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 29 384
+1961 */{ 0 , 2 , 15 , 43856 },/* 30 29 30 29 30 29 30 30 29 30 29 30 0 355
+1962 */{ 0 , 2 , 5 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+1963 */{ 4 , 1 , 25 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+1964 */{ 0 , 2 , 13 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 0 355
+1965 */{ 0 , 2 , 2 , 21088 },/* 29 30 29 30 29 29 30 29 29 30 30 29 0 353
+1966 */{ 3 , 1 , 21 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+1967 */{ 0 , 2 , 9 , 55632 },/* 30 30 29 30 30 29 29 30 29 30 29 30 0 355
+1968 */{ 7 , 1 , 30 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+1969 */{ 0 , 2 , 17 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+1970 */{ 0 , 2 , 6 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+1971 */{ 5 , 1 , 27 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+1972 */{ 0 , 2 , 15 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354
+1973 */{ 0 , 2 , 3 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+1974 */{ 4 , 1 , 23 , 53864 },/* 30 30 29 30 29 29 30 29 29 30 30 29 30 384
+1975 */{ 0 , 2 , 11 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+1976 */{ 8 , 1 , 31 , 54568 },/* 30 30 29 30 29 30 29 30 29 29 30 29 30 384
+1977 */{ 0 , 2 , 18 , 46400 },/* 30 29 30 30 29 30 29 30 29 30 29 29 0 354
+1978 */{ 0 , 2 , 7 , 46752 },/* 30 29 30 30 29 30 30 29 30 29 30 29 0 355
+1979 */{ 6 , 1 , 28 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 29 384
+1980 */{ 0 , 2 , 16 , 38320 },/* 30 29 29 30 29 30 29 30 30 29 30 30 0 355
+1981 */{ 0 , 2 , 5 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+1982 */{ 4 , 1 , 25 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+1983 */{ 0 , 2 , 13 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+1984 */{ 10 , 2 , 2 , 45656 },/* 30 29 30 30 29 29 30 29 29 30 29 30 30 384
+1985 */{ 0 , 2 , 20 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 0 354
+1986 */{ 0 , 2 , 9 , 27968 },/* 29 30 30 29 30 30 29 30 29 30 29 29 0 354
+1987 */{ 6 , 1 , 29 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 29 384
+1988 */{ 0 , 2 , 17 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1989 */{ 0 , 2 , 6 , 38256 },/* 30 29 29 30 29 30 29 30 29 30 30 30 0 355
+1990 */{ 5 , 1 , 27 , 18808 },/* 29 30 29 29 30 29 29 30 29 30 30 30 30 384
+1991 */{ 0 , 2 , 15 , 18800 },/* 29 30 29 29 30 29 29 30 29 30 30 30 0 354
+1992 */{ 0 , 2 , 4 , 25776 },/* 29 30 30 29 29 30 29 29 30 29 30 30 0 354
+1993 */{ 3 , 1 , 23 , 27216 },/* 29 30 30 29 30 29 30 29 29 30 29 30 29 383
+1994 */{ 0 , 2 , 10 , 59984 },/* 30 30 30 29 30 29 30 29 29 30 29 30 0 355
+1995 */{ 8 , 1 , 31 , 27432 },/* 29 30 30 29 30 29 30 30 29 29 30 29 30 384
+1996 */{ 0 , 2 , 19 , 23232 },/* 29 30 29 30 30 29 30 29 30 30 29 29 0 354
+1997 */{ 0 , 2 , 7 , 43872 },/* 30 29 30 29 30 29 30 30 29 30 30 29 0 355
+1998 */{ 5 , 1 , 28 , 37736 },/* 30 29 29 30 29 29 30 30 29 30 30 29 30 384
+1999 */{ 0 , 2 , 16 , 37600 },/* 30 29 29 30 29 29 30 29 30 30 30 29 0 354
+2000 */{ 0 , 2 , 5 , 51552 },/* 30 30 29 29 30 29 29 30 29 30 30 29 0 354
+2001 */{ 4 , 1 , 24 , 54440 },/* 30 30 29 30 29 30 29 29 30 29 30 29 30 384
+2002 */{ 0 , 2 , 12 , 54432 },/* 30 30 29 30 29 30 29 29 30 29 30 29 0 354
+2003 */{ 0 , 2 , 1 , 55888 },/* 30 30 29 30 30 29 30 29 29 30 29 30 0 355
+2004 */{ 2 , 1 , 22 , 23208 },/* 29 30 29 30 30 29 30 29 30 29 30 29 30 384
+2005 */{ 0 , 2 , 9 , 22176 },/* 29 30 29 30 29 30 30 29 30 29 30 29 0 354
+2006 */{ 7 , 1 , 29 , 43736 },/* 30 29 30 29 30 29 30 29 30 30 29 30 30 385
+2007 */{ 0 , 2 , 18 , 9680 },/* 29 29 30 29 29 30 29 30 30 30 29 30 0 354
+2008 */{ 0 , 2 , 7 , 37584 },/* 30 29 29 30 29 29 30 29 30 30 29 30 0 354
+2009 */{ 5 , 1 , 26 , 51544 },/* 30 30 29 29 30 29 29 30 29 30 29 30 30 384
+2010 */{ 0 , 2 , 14 , 43344 },/* 30 29 30 29 30 29 29 30 29 30 29 30 0 354
+2011 */{ 0 , 2 , 3 , 46240 },/* 30 29 30 30 29 30 29 29 30 29 30 29 0 354
+2012 */{ 4 , 1 , 23 , 46416 },/* 30 29 30 30 29 30 29 30 29 30 29 30 29 384
+2013 */{ 0 , 2 , 10 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+2014 */{ 9 , 1 , 31 , 21928 },/* 29 30 29 30 29 30 29 30 30 29 30 29 30 384
+2015 */{ 0 , 2 , 19 , 19360 },/* 29 30 29 29 30 29 30 30 30 29 30 29 0 354
+2016 */{ 0 , 2 , 8 , 42416 },/* 30 29 30 29 29 30 29 30 30 29 30 30 0 355
+2017 */{ 6 , 1 , 28 , 21176 },/* 29 30 29 30 29 29 30 29 30 29 30 30 30 384
+2018 */{ 0 , 2 , 16 , 21168 },/* 29 30 29 30 29 29 30 29 30 29 30 30 0 354
+2019 */{ 0 , 2 , 5 , 43312 },/* 30 29 30 29 30 29 29 30 29 29 30 30 0 354
+2020 */{ 4 , 1 , 25 , 29864 },/* 29 30 30 30 29 30 29 29 30 29 30 29 30 384
+2021 */{ 0 , 2 , 12 , 27296 },/* 29 30 30 29 30 29 30 29 30 29 30 29 0 354
+2022 */{ 0 , 2 , 1 , 44368 },/* 30 29 30 29 30 30 29 30 29 30 29 30 0 355
+2023 */{ 2 , 1 , 22 , 19880 },/* 29 30 29 29 30 30 29 30 30 29 30 29 30 384
+2024 */{ 0 , 2 , 10 , 19296 },/* 29 30 29 29 30 29 30 30 29 30 30 29 0 354
+2025 */{ 6 , 1 , 29 , 42352 },/* 30 29 30 29 29 30 29 30 29 30 30 30 29 384
+2026 */{ 0 , 2 , 17 , 42208 },/* 30 29 30 29 29 30 29 29 30 30 30 29 0 354
+2027 */{ 0 , 2 , 6 , 53856 },/* 30 30 29 30 29 29 30 29 29 30 30 29 0 354
+2028 */{ 5 , 1 , 26 , 59696 },/* 30 30 30 29 30 29 29 30 29 29 30 30 29 384
+2029 */{ 0 , 2 , 13 , 54576 },/* 30 30 29 30 29 30 29 30 29 29 30 30 0 355
+2030 */{ 0 , 2 , 3 , 23200 },/* 29 30 29 30 30 29 30 29 30 29 30 29 0 354
+2031 */{ 3 , 1 , 23 , 27472 },/* 29 30 30 29 30 29 30 30 29 30 29 30 29 384
+2032 */{ 0 , 2 , 11 , 38608 },/* 30 29 29 30 29 30 30 29 30 30 29 30 0 355
+2033 */{ 11 , 1 , 31 , 19176 },/* 29 30 29 29 30 29 30 29 30 30 30 29 30 384
+2034 */{ 0 , 2 , 19 , 19152 },/* 29 30 29 29 30 29 30 29 30 30 29 30 0 354
+2035 */{ 0 , 2 , 8 , 42192 },/* 30 29 30 29 29 30 29 29 30 30 29 30 0 354
+2036 */{ 6 , 1 , 28 , 53848 },/* 30 30 29 30 29 29 30 29 29 30 29 30 30 384
+2037 */{ 0 , 2 , 15 , 53840 },/* 30 30 29 30 29 29 30 29 29 30 29 30 0 354
+2038 */{ 0 , 2 , 4 , 54560 },/* 30 30 29 30 29 30 29 30 29 29 30 29 0 354
+2039 */{ 5 , 1 , 24 , 55968 },/* 30 30 29 30 30 29 30 29 30 29 30 29 29 384
+2040 */{ 0 , 2 , 12 , 46496 },/* 30 29 30 30 29 30 29 30 30 29 30 29 0 355
+2041 */{ 0 , 2 , 1 , 22224 },/* 29 30 29 30 29 30 30 29 30 30 29 30 0 355
+2042 */{ 2 , 1 , 22 , 19160 },/* 29 30 29 29 30 29 30 29 30 30 29 30 30 384
+2043 */{ 0 , 2 , 10 , 18864 },/* 29 30 29 29 30 29 29 30 30 29 30 30 0 354
+2044 */{ 7 , 1 , 30 , 42168 },/* 30 29 30 29 29 30 29 29 30 29 30 30 30 384
+2045 */{ 0 , 2 , 17 , 42160 },/* 30 29 30 29 29 30 29 29 30 29 30 30 0 354
+2046 */{ 0 , 2 , 6 , 43600 },/* 30 29 30 29 30 29 30 29 29 30 29 30 0 354
+2047 */{ 5 , 1 , 26 , 46376 },/* 30 29 30 30 29 30 29 30 29 29 30 29 30 384
+2048 */{ 0 , 2 , 14 , 27936 },/* 29 30 30 29 30 30 29 30 29 29 30 29 0 354
+2049 */{ 0 , 2 , 2 , 44448 },/* 30 29 30 29 30 30 29 30 30 29 30 29 0 355
+2050 */{ 3 , 1 , 23 , 21936 },/* 29 30 29 30 29 30 29 30 30 29 30 30 29 384
+ */};
+
+
+ internal override int MinCalendarYear
+ {
+ get
+ {
+ return (MIN_LUNISOLAR_YEAR);
+ }
+ }
+
+ internal override int MaxCalendarYear
+ {
+ get
+ {
+ return (MAX_LUNISOLAR_YEAR);
+ }
+ }
+
+ internal override DateTime MinDate
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+ internal override DateTime MaxDate
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+
+ internal override EraInfo[] CalEraInfo
+ {
+ get
+ {
+ return (taiwanLunisolarEraInfo);
+ }
+ }
+
+ internal override int GetYearInfo(int lunarYear, int index)
+ {
+ if ((lunarYear < MIN_LUNISOLAR_YEAR) || (lunarYear > MAX_LUNISOLAR_YEAR))
+ {
+ throw new ArgumentOutOfRangeException(
+ "year",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ MIN_LUNISOLAR_YEAR,
+ MAX_LUNISOLAR_YEAR));
+ }
+ Contract.EndContractBlock();
+
+ return s_yinfo[lunarYear - MIN_LUNISOLAR_YEAR, index];
+ }
+
+ internal override int GetYear(int year, DateTime time)
+ {
+ return helper.GetYear(year, time);
+ }
+
+ internal override int GetGregorianYear(int year, int era)
+ {
+ return helper.GetGregorianYear(year, era);
+ }
+
+ public TaiwanLunisolarCalendar()
+ {
+ helper = new GregorianCalendarHelper(this, taiwanLunisolarEraInfo);
+ }
+
+ public override int GetEra(DateTime time)
+ {
+ return (helper.GetEra(time));
+ }
+
+ internal override CalendarId BaseCalendarID
+ {
+ get
+ {
+ return (CalendarId.TAIWAN);
+ }
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return (CalendarId.TAIWANLUNISOLAR);
+ }
+ }
+
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (helper.Eras);
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/ThaiBuddhistCalendar.cs b/src/mscorlib/shared/System/Globalization/ThaiBuddhistCalendar.cs
new file mode 100644
index 0000000000..9e6e30406c
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/ThaiBuddhistCalendar.cs
@@ -0,0 +1,236 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.CodeAnalysis;
+using System.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ /*=================================ThaiBuddhistCalendar==========================
+ **
+ ** ThaiBuddhistCalendar is based on Gregorian calendar. Its year value has
+ ** an offset to the Gregorain calendar.
+ **
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 0001/01/01 9999/12/31
+ ** Thai 0544/01/01 10542/12/31
+ ============================================================================*/
+
+ [Serializable]
+ public class ThaiBuddhistCalendar : Calendar
+ {
+ // Initialize our era info.
+ internal static EraInfo[] thaiBuddhistEraInfo = new EraInfo[] {
+ new EraInfo( 1, 1, 1, 1, -543, 544, GregorianCalendar.MaxYear + 543) // era #, start year/month/day, yearOffset, minEraYear
+ };
+
+ //
+ // The era value for the current era.
+ //
+
+ public const int ThaiBuddhistEra = 1;
+
+ internal GregorianCalendarHelper helper;
+
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (DateTime.MinValue);
+ }
+ }
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (DateTime.MaxValue);
+ }
+ }
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.SolarCalendar;
+ }
+ }
+
+ public ThaiBuddhistCalendar()
+ {
+ helper = new GregorianCalendarHelper(this, thaiBuddhistEraInfo);
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return (CalendarId.THAI);
+ }
+ }
+
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ return (helper.AddMonths(time, months));
+ }
+
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ return (helper.AddYears(time, years));
+ }
+
+
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ return (helper.GetDaysInMonth(year, month, era));
+ }
+
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ return (helper.GetDaysInYear(year, era));
+ }
+
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ return (helper.GetDayOfMonth(time));
+ }
+
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ return (helper.GetDayOfWeek(time));
+ }
+
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ return (helper.GetDayOfYear(time));
+ }
+
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ return (helper.GetMonthsInYear(year, era));
+ }
+
+
+ [SuppressMessage("Microsoft.Contracts", "CC1055")] // Skip extra error checking to avoid *potential* AppCompat problems.
+ public override int GetWeekOfYear(DateTime time, CalendarWeekRule rule, DayOfWeek firstDayOfWeek)
+ {
+ return (helper.GetWeekOfYear(time, rule, firstDayOfWeek));
+ }
+
+
+ public override int GetEra(DateTime time)
+ {
+ return (helper.GetEra(time));
+ }
+
+ public override int GetMonth(DateTime time)
+ {
+ return (helper.GetMonth(time));
+ }
+
+
+ public override int GetYear(DateTime time)
+ {
+ return (helper.GetYear(time));
+ }
+
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ return (helper.IsLeapDay(year, month, day, era));
+ }
+
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ return (helper.IsLeapYear(year, era));
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this calendar does not have leap month, or this year is not a leap year.
+ //
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ return (helper.GetLeapMonth(year, era));
+ }
+
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ return (helper.IsLeapMonth(year, month, era));
+ }
+
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ return (helper.ToDateTime(year, month, day, hour, minute, second, millisecond, era));
+ }
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (helper.Eras);
+ }
+ }
+
+ private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 2572;
+
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ if (twoDigitYearMax == -1)
+ {
+ twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
+ }
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ VerifyWritable();
+ if (value < 99 || value > helper.MaxYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ "year",
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ 99,
+ helper.MaxYear));
+ }
+ twoDigitYearMax = value;
+ }
+ }
+
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.EndContractBlock();
+
+ return (helper.ToFourDigitYear(year, this.TwoDigitYearMax));
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Globalization/TimeSpanStyles.cs b/src/mscorlib/shared/System/Globalization/TimeSpanStyles.cs
new file mode 100644
index 0000000000..68a47bcbe6
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/TimeSpanStyles.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ [Flags]
+ public enum TimeSpanStyles
+ {
+ None = 0x00000000,
+ AssumeNegative = 0x00000001,
+ }
+}
diff --git a/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs b/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs
new file mode 100644
index 0000000000..b7ba6d0112
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/UmAlQuraCalendar.cs
@@ -0,0 +1,865 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Diagnostics.Contracts;
+
+namespace System.Globalization
+{
+ /*
+ ** Calendar support range:
+ ** Calendar Minimum Maximum
+ ** ========== ========== ==========
+ ** Gregorian 1900/04/30 2077/05/13
+ ** UmAlQura 1318/01/01 1500/12/30
+ */
+
+ [Serializable]
+ public partial class UmAlQuraCalendar : Calendar
+ {
+ internal const int MinCalendarYear = 1318;
+ internal const int MaxCalendarYear = 1500;
+
+ internal struct DateMapping
+ {
+ internal DateMapping(int MonthsLengthFlags, int GYear, int GMonth, int GDay)
+ {
+ HijriMonthsLengthFlags = MonthsLengthFlags;
+ GregorianDate = new DateTime(GYear, GMonth, GDay);
+ }
+ internal int HijriMonthsLengthFlags;
+ internal DateTime GregorianDate;
+ }
+
+ private static readonly DateMapping[] s_hijriYearInfo = InitDateMapping();
+
+ private static DateMapping[] InitDateMapping()
+ {
+ short[] rawData = new short[]
+ {
+ //These data is taken from Tables/Excel/UmAlQura.xls please make sure that the two places are in sync
+ /* DaysPerM GY GM GD D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12
+ 1318*/0x02EA, 1900, 4, 30,/* 0 1 0 1 0 1 1 1 0 1 0 0 4/30/1900
+ 1319*/0x06E9, 1901, 4, 19,/* 1 0 0 1 0 1 1 1 0 1 1 0 4/19/1901
+ 1320*/0x0ED2, 1902, 4, 9,/* 0 1 0 0 1 0 1 1 0 1 1 1 4/9/1902
+ 1321*/0x0EA4, 1903, 3, 30,/* 0 0 1 0 0 1 0 1 0 1 1 1 3/30/1903
+ 1322*/0x0D4A, 1904, 3, 18,/* 0 1 0 1 0 0 1 0 1 0 1 1 3/18/1904
+ 1323*/0x0A96, 1905, 3, 7,/* 0 1 1 0 1 0 0 1 0 1 0 1 3/7/1905
+ 1324*/0x0536, 1906, 2, 24,/* 0 1 1 0 1 1 0 0 1 0 1 0 2/24/1906
+ 1325*/0x0AB5, 1907, 2, 13,/* 1 0 1 0 1 1 0 1 0 1 0 1 2/13/1907
+ 1326*/0x0DAA, 1908, 2, 3,/* 0 1 0 1 0 1 0 1 1 0 1 1 2/3/1908
+ 1327*/0x0BA4, 1909, 1, 23,/* 0 0 1 0 0 1 0 1 1 1 0 1 1/23/1909
+ 1328*/0x0B49, 1910, 1, 12,/* 1 0 0 1 0 0 1 0 1 1 0 1 1/12/1910
+ 1329*/0x0A93, 1911, 1, 1,/* 1 1 0 0 1 0 0 1 0 1 0 1 1/1/1911
+ 1330*/0x052B, 1911, 12, 21,/* 1 1 0 1 0 1 0 0 1 0 1 0 12/21/1911
+ 1331*/0x0A57, 1912, 12, 9,/* 1 1 1 0 1 0 1 0 0 1 0 1 12/9/1912
+ 1332*/0x04B6, 1913, 11, 29,/* 0 1 1 0 1 1 0 1 0 0 1 0 11/29/1913
+ 1333*/0x0AB5, 1914, 11, 18,/* 1 0 1 0 1 1 0 1 0 1 0 1 11/18/1914
+ 1334*/0x05AA, 1915, 11, 8,/* 0 1 0 1 0 1 0 1 1 0 1 0 11/8/1915
+ 1335*/0x0D55, 1916, 10, 27,/* 1 0 1 0 1 0 1 0 1 0 1 1 10/27/1916
+ 1336*/0x0D2A, 1917, 10, 17,/* 0 1 0 1 0 1 0 0 1 0 1 1 10/17/1917
+ 1337*/0x0A56, 1918, 10, 6,/* 0 1 1 0 1 0 1 0 0 1 0 1 10/6/1918
+ 1338*/0x04AE, 1919, 9, 25,/* 0 1 1 1 0 1 0 1 0 0 1 0 9/25/1919
+ 1339*/0x095D, 1920, 9, 13,/* 1 0 1 1 1 0 1 0 1 0 0 1 9/13/1920
+ 1340*/0x02EC, 1921, 9, 3,/* 0 0 1 1 0 1 1 1 0 1 0 0 9/3/1921
+ 1341*/0x06D5, 1922, 8, 23,/* 1 0 1 0 1 0 1 1 0 1 1 0 8/23/1922
+ 1342*/0x06AA, 1923, 8, 13,/* 0 1 0 1 0 1 0 1 0 1 1 0 8/13/1923
+ 1343*/0x0555, 1924, 8, 1,/* 1 0 1 0 1 0 1 0 1 0 1 0 8/1/1924
+ 1344*/0x04AB, 1925, 7, 21,/* 1 1 0 1 0 1 0 1 0 0 1 0 7/21/1925
+ 1345*/0x095B, 1926, 7, 10,/* 1 1 0 1 1 0 1 0 1 0 0 1 7/10/1926
+ 1346*/0x02BA, 1927, 6, 30,/* 0 1 0 1 1 1 0 1 0 1 0 0 6/30/1927
+ 1347*/0x0575, 1928, 6, 18,/* 1 0 1 0 1 1 1 0 1 0 1 0 6/18/1928
+ 1348*/0x0BB2, 1929, 6, 8,/* 0 1 0 0 1 1 0 1 1 1 0 1 6/8/1929
+ 1349*/0x0764, 1930, 5, 29,/* 0 0 1 0 0 1 1 0 1 1 1 0 5/29/1930
+ 1350*/0x0749, 1931, 5, 18,/* 1 0 0 1 0 0 1 0 1 1 1 0 5/18/1931
+ 1351*/0x0655, 1932, 5, 6,/* 1 0 1 0 1 0 1 0 0 1 1 0 5/6/1932
+ 1352*/0x02AB, 1933, 4, 25,/* 1 1 0 1 0 1 0 1 0 1 0 0 4/25/1933
+ 1353*/0x055B, 1934, 4, 14,/* 1 1 0 1 1 0 1 0 1 0 1 0 4/14/1934
+ 1354*/0x0ADA, 1935, 4, 4,/* 0 1 0 1 1 0 1 1 0 1 0 1 4/4/1935
+ 1355*/0x06D4, 1936, 3, 24,/* 0 0 1 0 1 0 1 1 0 1 1 0 3/24/1936
+ 1356*/0x0EC9, 1937, 3, 13,/* 1 0 0 1 0 0 1 1 0 1 1 1 3/13/1937
+ 1357*/0x0D92, 1938, 3, 3,/* 0 1 0 0 1 0 0 1 1 0 1 1 3/3/1938
+ 1358*/0x0D25, 1939, 2, 20,/* 1 0 1 0 0 1 0 0 1 0 1 1 2/20/1939
+ 1359*/0x0A4D, 1940, 2, 9,/* 1 0 1 1 0 0 1 0 0 1 0 1 2/9/1940
+ 1360*/0x02AD, 1941, 1, 28,/* 1 0 1 1 0 1 0 1 0 1 0 0 1/28/1941
+ 1361*/0x056D, 1942, 1, 17,/* 1 0 1 1 0 1 1 0 1 0 1 0 1/17/1942
+ 1362*/0x0B6A, 1943, 1, 7,/* 0 1 0 1 0 1 1 0 1 1 0 1 1/7/1943
+ 1363*/0x0B52, 1943, 12, 28,/* 0 1 0 0 1 0 1 0 1 1 0 1 12/28/1943
+ 1364*/0x0AA5, 1944, 12, 16,/* 1 0 1 0 0 1 0 1 0 1 0 1 12/16/1944
+ 1365*/0x0A4B, 1945, 12, 5,/* 1 1 0 1 0 0 1 0 0 1 0 1 12/5/1945
+ 1366*/0x0497, 1946, 11, 24,/* 1 1 1 0 1 0 0 1 0 0 1 0 11/24/1946
+ 1367*/0x0937, 1947, 11, 13,/* 1 1 1 0 1 1 0 0 1 0 0 1 11/13/1947
+ 1368*/0x02B6, 1948, 11, 2,/* 0 1 1 0 1 1 0 1 0 1 0 0 11/2/1948
+ 1369*/0x0575, 1949, 10, 22,/* 1 0 1 0 1 1 1 0 1 0 1 0 10/22/1949
+ 1370*/0x0D6A, 1950, 10, 12,/* 0 1 0 1 0 1 1 0 1 0 1 1 10/12/1950
+ 1371*/0x0D52, 1951, 10, 2,/* 0 1 0 0 1 0 1 0 1 0 1 1 10/2/1951
+ 1372*/0x0A96, 1952, 9, 20,/* 0 1 1 0 1 0 0 1 0 1 0 1 9/20/1952
+ 1373*/0x092D, 1953, 9, 9,/* 1 0 1 1 0 1 0 0 1 0 0 1 9/9/1953
+ 1374*/0x025D, 1954, 8, 29,/* 1 0 1 1 1 0 1 0 0 1 0 0 8/29/1954
+ 1375*/0x04DD, 1955, 8, 18,/* 1 0 1 1 1 0 1 1 0 0 1 0 8/18/1955
+ 1376*/0x0ADA, 1956, 8, 7,/* 0 1 0 1 1 0 1 1 0 1 0 1 8/7/1956
+ 1377*/0x05D4, 1957, 7, 28,/* 0 0 1 0 1 0 1 1 1 0 1 0 7/28/1957
+ 1378*/0x0DA9, 1958, 7, 17,/* 1 0 0 1 0 1 0 1 1 0 1 1 7/17/1958
+ 1379*/0x0D52, 1959, 7, 7,/* 0 1 0 0 1 0 1 0 1 0 1 1 7/7/1959
+ 1380*/0x0AAA, 1960, 6, 25,/* 0 1 0 1 0 1 0 1 0 1 0 1 6/25/1960
+ 1381*/0x04D6, 1961, 6, 14,/* 0 1 1 0 1 0 1 1 0 0 1 0 6/14/1961
+ 1382*/0x09B6, 1962, 6, 3,/* 0 1 1 0 1 1 0 1 1 0 0 1 6/3/1962
+ 1383*/0x0374, 1963, 5, 24,/* 0 0 1 0 1 1 1 0 1 1 0 0 5/24/1963
+ 1384*/0x0769, 1964, 5, 12,/* 1 0 0 1 0 1 1 0 1 1 1 0 5/12/1964
+ 1385*/0x0752, 1965, 5, 2,/* 0 1 0 0 1 0 1 0 1 1 1 0 5/2/1965
+ 1386*/0x06A5, 1966, 4, 21,/* 1 0 1 0 0 1 0 1 0 1 1 0 4/21/1966
+ 1387*/0x054B, 1967, 4, 10,/* 1 1 0 1 0 0 1 0 1 0 1 0 4/10/1967
+ 1388*/0x0AAB, 1968, 3, 29,/* 1 1 0 1 0 1 0 1 0 1 0 1 3/29/1968
+ 1389*/0x055A, 1969, 3, 19,/* 0 1 0 1 1 0 1 0 1 0 1 0 3/19/1969
+ 1390*/0x0AD5, 1970, 3, 8,/* 1 0 1 0 1 0 1 1 0 1 0 1 3/8/1970
+ 1391*/0x0DD2, 1971, 2, 26,/* 0 1 0 0 1 0 1 1 1 0 1 1 2/26/1971
+ 1392*/0x0DA4, 1972, 2, 16,/* 0 0 1 0 0 1 0 1 1 0 1 1 2/16/1972
+ 1393*/0x0D49, 1973, 2, 4,/* 1 0 0 1 0 0 1 0 1 0 1 1 2/4/1973
+ 1394*/0x0A95, 1974, 1, 24,/* 1 0 1 0 1 0 0 1 0 1 0 1 1/24/1974
+ 1395*/0x052D, 1975, 1, 13,/* 1 0 1 1 0 1 0 0 1 0 1 0 1/13/1975
+ 1396*/0x0A5D, 1976, 1, 2,/* 1 0 1 1 1 0 1 0 0 1 0 1 1/2/1976
+ 1397*/0x055A, 1976, 12, 22,/* 0 1 0 1 1 0 1 0 1 0 1 0 12/22/1976
+ 1398*/0x0AD5, 1977, 12, 11,/* 1 0 1 0 1 0 1 1 0 1 0 1 12/11/1977
+ 1399*/0x06AA, 1978, 12, 1,/* 0 1 0 1 0 1 0 1 0 1 1 0 12/1/1978
+ 1400*/0x0695, 1979, 11, 20,/* 1 0 1 0 1 0 0 1 0 1 1 0 11/20/1979
+ 1401*/0x052B, 1980, 11, 8,/* 1 1 0 1 0 1 0 0 1 0 1 0 11/8/1980
+ 1402*/0x0A57, 1981, 10, 28,/* 1 1 1 0 1 0 1 0 0 1 0 1 10/28/1981
+ 1403*/0x04AE, 1982, 10, 18,/* 0 1 1 1 0 1 0 1 0 0 1 0 10/18/1982
+ 1404*/0x0976, 1983, 10, 7,/* 0 1 1 0 1 1 1 0 1 0 0 1 10/7/1983
+ 1405*/0x056C, 1984, 9, 26,/* 0 0 1 1 0 1 1 0 1 0 1 0 9/26/1984
+ 1406*/0x0B55, 1985, 9, 15,/* 1 0 1 0 1 0 1 0 1 1 0 1 9/15/1985
+ 1407*/0x0AAA, 1986, 9, 5,/* 0 1 0 1 0 1 0 1 0 1 0 1 9/5/1986
+ 1408*/0x0A55, 1987, 8, 25,/* 1 0 1 0 1 0 1 0 0 1 0 1 8/25/1987
+ 1409*/0x04AD, 1988, 8, 13,/* 1 0 1 1 0 1 0 1 0 0 1 0 8/13/1988
+ 1410*/0x095D, 1989, 8, 2,/* 1 0 1 1 1 0 1 0 1 0 0 1 8/2/1989
+ 1411*/0x02DA, 1990, 7, 23,/* 0 1 0 1 1 0 1 1 0 1 0 0 7/23/1990
+ 1412*/0x05D9, 1991, 7, 12,/* 1 0 0 1 1 0 1 1 1 0 1 0 7/12/1991
+ 1413*/0x0DB2, 1992, 7, 1,/* 0 1 0 0 1 1 0 1 1 0 1 1 7/1/1992
+ 1414*/0x0BA4, 1993, 6, 21,/* 0 0 1 0 0 1 0 1 1 1 0 1 6/21/1993
+ 1415*/0x0B4A, 1994, 6, 10,/* 0 1 0 1 0 0 1 0 1 1 0 1 6/10/1994
+ 1416*/0x0A55, 1995, 5, 30,/* 1 0 1 0 1 0 1 0 0 1 0 1 5/30/1995
+ 1417*/0x02B5, 1996, 5, 18,/* 1 0 1 0 1 1 0 1 0 1 0 0 5/18/1996
+ 1418*/0x0575, 1997, 5, 7,/* 1 0 1 0 1 1 1 0 1 0 1 0 5/7/1997
+ 1419*/0x0B6A, 1998, 4, 27,/* 0 1 0 1 0 1 1 0 1 1 0 1 4/27/1998
+ 1420*/0x0BD2, 1999, 4, 17,/* 0 1 0 0 1 0 1 1 1 1 0 1 4/17/1999
+ 1421*/0x0BC4, 2000, 4, 6,/* 0 0 1 0 0 0 1 1 1 1 0 1 4/6/2000
+ 1422*/0x0B89, 2001, 3, 26,/* 1 0 0 1 0 0 0 1 1 1 0 1 3/26/2001
+ 1423*/0x0A95, 2002, 3, 15,/* 1 0 1 0 1 0 0 1 0 1 0 1 3/15/2002
+ 1424*/0x052D, 2003, 3, 4,/* 1 0 1 1 0 1 0 0 1 0 1 0 3/4/2003
+ 1425*/0x05AD, 2004, 2, 21,/* 1 0 1 1 0 1 0 1 1 0 1 0 2/21/2004
+ 1426*/0x0B6A, 2005, 2, 10,/* 0 1 0 1 0 1 1 0 1 1 0 1 2/10/2005
+ 1427*/0x06D4, 2006, 1, 31,/* 0 0 1 0 1 0 1 1 0 1 1 0 1/31/2006
+ 1428*/0x0DC9, 2007, 1, 20,/* 1 0 0 1 0 0 1 1 1 0 1 1 1/20/2007
+ 1429*/0x0D92, 2008, 1, 10,/* 0 1 0 0 1 0 0 1 1 0 1 1 1/10/2008
+ 1430*/0x0AA6, 2008, 12, 29,/* 0 1 1 0 0 1 0 1 0 1 0 1 12/29/2008
+ 1431*/0x0956, 2009, 12, 18,/* 0 1 1 0 1 0 1 0 1 0 0 1 12/18/2009
+ 1432*/0x02AE, 2010, 12, 7,/* 0 1 1 1 0 1 0 1 0 1 0 0 12/7/2010
+ 1433*/0x056D, 2011, 11, 26,/* 1 0 1 1 0 1 1 0 1 0 1 0 11/26/2011
+ 1434*/0x036A, 2012, 11, 15,/* 0 1 0 1 0 1 1 0 1 1 0 0 11/15/2012
+ 1435*/0x0B55, 2013, 11, 4,/* 1 0 1 0 1 0 1 0 1 1 0 1 11/4/2013
+ 1436*/0x0AAA, 2014, 10, 25,/* 0 1 0 1 0 1 0 1 0 1 0 1 10/25/2014
+ 1437*/0x094D, 2015, 10, 14,/* 1 0 1 1 0 0 1 0 1 0 0 1 10/14/2015
+ 1438*/0x049D, 2016, 10, 2,/* 1 0 1 1 1 0 0 1 0 0 1 0 10/2/2016
+ 1439*/0x095D, 2017, 9, 21,/* 1 0 1 1 1 0 1 0 1 0 0 1 9/21/2017
+ 1440*/0x02BA, 2018, 9, 11,/* 0 1 0 1 1 1 0 1 0 1 0 0 9/11/2018
+ 1441*/0x05B5, 2019, 8, 31,/* 1 0 1 0 1 1 0 1 1 0 1 0 8/31/2019
+ 1442*/0x05AA, 2020, 8, 20,/* 0 1 0 1 0 1 0 1 1 0 1 0 8/20/2020
+ 1443*/0x0D55, 2021, 8, 9,/* 1 0 1 0 1 0 1 0 1 0 1 1 8/9/2021
+ 1444*/0x0A9A, 2022, 7, 30,/* 0 1 0 1 1 0 0 1 0 1 0 1 7/30/2022
+ 1445*/0x092E, 2023, 7, 19,/* 0 1 1 1 0 1 0 0 1 0 0 1 7/19/2023
+ 1446*/0x026E, 2024, 7, 7,/* 0 1 1 1 0 1 1 0 0 1 0 0 7/7/2024
+ 1447*/0x055D, 2025, 6, 26,/* 1 0 1 1 1 0 1 0 1 0 1 0 6/26/2025
+ 1448*/0x0ADA, 2026, 6, 16,/* 0 1 0 1 1 0 1 1 0 1 0 1 6/16/2026
+ 1449*/0x06D4, 2027, 6, 6,/* 0 0 1 0 1 0 1 1 0 1 1 0 6/6/2027
+ 1450*/0x06A5, 2028, 5, 25,/* 1 0 1 0 0 1 0 1 0 1 1 0 5/25/2028
+ 1451*/0x054B, 2029, 5, 14,/* 1 1 0 1 0 0 1 0 1 0 1 0 5/14/2029
+ 1452*/0x0A97, 2030, 5, 3,/* 1 1 1 0 1 0 0 1 0 1 0 1 5/3/2030
+ 1453*/0x054E, 2031, 4, 23,/* 0 1 1 1 0 0 1 0 1 0 1 0 4/23/2031
+ 1454*/0x0AAE, 2032, 4, 11,/* 0 1 1 1 0 1 0 1 0 1 0 1 4/11/2032
+ 1455*/0x05AC, 2033, 4, 1,/* 0 0 1 1 0 1 0 1 1 0 1 0 4/1/2033
+ 1456*/0x0BA9, 2034, 3, 21,/* 1 0 0 1 0 1 0 1 1 1 0 1 3/21/2034
+ 1457*/0x0D92, 2035, 3, 11,/* 0 1 0 0 1 0 0 1 1 0 1 1 3/11/2035
+ 1458*/0x0B25, 2036, 2, 28,/* 1 0 1 0 0 1 0 0 1 1 0 1 2/28/2036
+ 1459*/0x064B, 2037, 2, 16,/* 1 1 0 1 0 0 1 0 0 1 1 0 2/16/2037
+ 1460*/0x0CAB, 2038, 2, 5,/* 1 1 0 1 0 1 0 1 0 0 1 1 2/5/2038
+ 1461*/0x055A, 2039, 1, 26,/* 0 1 0 1 1 0 1 0 1 0 1 0 1/26/2039
+ 1462*/0x0B55, 2040, 1, 15,/* 1 0 1 0 1 0 1 0 1 1 0 1 1/15/2040
+ 1463*/0x06D2, 2041, 1, 4,/* 0 1 0 0 1 0 1 1 0 1 1 0 1/4/2041
+ 1464*/0x0EA5, 2041, 12, 24,/* 1 0 1 0 0 1 0 1 0 1 1 1 12/24/2041
+ 1465*/0x0E4A, 2042, 12, 14,/* 0 1 0 1 0 0 1 0 0 1 1 1 12/14/2042
+ 1466*/0x0A95, 2043, 12, 3,/* 1 0 1 0 1 0 0 1 0 1 0 1 12/3/2043
+ 1467*/0x052D, 2044, 11, 21,/* 1 0 1 1 0 1 0 0 1 0 1 0 11/21/2044
+ 1468*/0x0AAD, 2045, 11, 10,/* 1 0 1 1 0 1 0 1 0 1 0 1 11/10/2045
+ 1469*/0x036C, 2046, 10, 31,/* 0 0 1 1 0 1 1 0 1 1 0 0 10/31/2046
+ 1470*/0x0759, 2047, 10, 20,/* 1 0 0 1 1 0 1 0 1 1 1 0 10/20/2047
+ 1471*/0x06D2, 2048, 10, 9,/* 0 1 0 0 1 0 1 1 0 1 1 0 10/9/2048
+ 1472*/0x0695, 2049, 9, 28,/* 1 0 1 0 1 0 0 1 0 1 1 0 9/28/2049
+ 1473*/0x052D, 2050, 9, 17,/* 1 0 1 1 0 1 0 0 1 0 1 0 9/17/2050
+ 1474*/0x0A5B, 2051, 9, 6,/* 1 1 0 1 1 0 1 0 0 1 0 1 9/6/2051
+ 1475*/0x04BA, 2052, 8, 26,/* 0 1 0 1 1 1 0 1 0 0 1 0 8/26/2052
+ 1476*/0x09BA, 2053, 8, 15,/* 0 1 0 1 1 1 0 1 1 0 0 1 8/15/2053
+ 1477*/0x03B4, 2054, 8, 5,/* 0 0 1 0 1 1 0 1 1 1 0 0 8/5/2054
+ 1478*/0x0B69, 2055, 7, 25,/* 1 0 0 1 0 1 1 0 1 1 0 1 7/25/2055
+ 1479*/0x0B52, 2056, 7, 14,/* 0 1 0 0 1 0 1 0 1 1 0 1 7/14/2056
+ 1480*/0x0AA6, 2057, 7, 3,/* 0 1 1 0 0 1 0 1 0 1 0 1 7/3/2057
+ 1481*/0x04B6, 2058, 6, 22,/* 0 1 1 0 1 1 0 1 0 0 1 0 6/22/2058
+ 1482*/0x096D, 2059, 6, 11,/* 1 0 1 1 0 1 1 0 1 0 0 1 6/11/2059
+ 1483*/0x02EC, 2060, 5, 31,/* 0 0 1 1 0 1 1 1 0 1 0 0 5/31/2060
+ 1484*/0x06D9, 2061, 5, 20,/* 1 0 0 1 1 0 1 1 0 1 1 0 5/20/2061
+ 1485*/0x0EB2, 2062, 5, 10,/* 0 1 0 0 1 1 0 1 0 1 1 1 5/10/2062
+ 1486*/0x0D54, 2063, 4, 30,/* 0 0 1 0 1 0 1 0 1 0 1 1 4/30/2063
+ 1487*/0x0D2A, 2064, 4, 18,/* 0 1 0 1 0 1 0 0 1 0 1 1 4/18/2064
+ 1488*/0x0A56, 2065, 4, 7,/* 0 1 1 0 1 0 1 0 0 1 0 1 4/7/2065
+ 1489*/0x04AE, 2066, 3, 27,/* 0 1 1 1 0 1 0 1 0 0 1 0 3/27/2066
+ 1490*/0x096D, 2067, 3, 16,/* 1 0 1 1 0 1 1 0 1 0 0 1 3/16/2067
+ 1491*/0x0D6A, 2068, 3, 5,/* 0 1 0 1 0 1 1 0 1 0 1 1 3/5/2068
+ 1492*/0x0B54, 2069, 2, 23,/* 0 0 1 0 1 0 1 0 1 1 0 1 2/23/2069
+ 1493*/0x0B29, 2070, 2, 12,/* 1 0 0 1 0 1 0 0 1 1 0 1 2/12/2070
+ 1494*/0x0A93, 2071, 2, 1,/* 1 1 0 0 1 0 0 1 0 1 0 1 2/1/2071
+ 1495*/0x052B, 2072, 1, 21,/* 1 1 0 1 0 1 0 0 1 0 1 0 1/21/2072
+ 1496*/0x0A57, 2073, 1, 9,/* 1 1 1 0 1 0 1 0 0 1 0 1 1/9/2073
+ 1497*/0x0536, 2073, 12, 30,/* 0 1 1 0 1 1 0 0 1 0 1 0 12/30/2073
+ 1498*/0x0AB5, 2074, 12, 19,/* 1 0 1 0 1 1 0 1 0 1 0 1 12/19/2074
+ 1499*/0x06AA, 2075, 12, 9,/* 0 1 0 1 0 1 0 1 0 1 1 0 12/9/2075
+ 1500*/0x0E93, 2076, 11, 27,/* 1 1 0 0 1 0 0 1 0 1 1 1 11/27/2076
+ 1501*/ 0, 2077, 11, 17,/* 0 0 0 0 0 0 0 0 0 0 0 0 11/17/2077
+ */ };
+ // Direct inline initialization of DateMapping array would produce a lot of code bloat.
+
+ // We take advantage of C# compiler compiles inline initialization of primitive type array into very compact code.
+ // So we start with raw data stored in primitive type array, and initialize the DateMapping out of it
+
+ DateMapping[] mapping = new DateMapping[rawData.Length / 4];
+ for (int i = 0; i < mapping.Length; i++)
+ mapping[i] = new DateMapping(rawData[i * 4], rawData[i * 4 + 1], rawData[i * 4 + 2], rawData[i * 4 + 3]);
+ return mapping;
+ }
+
+ public const int UmAlQuraEra = 1;
+
+ internal const int DateCycle = 30;
+ internal const int DatePartYear = 0;
+ internal const int DatePartDayOfYear = 1;
+ internal const int DatePartMonth = 2;
+ internal const int DatePartDay = 3;
+
+
+ // This is the minimal Gregorian date that we support in the UmAlQuraCalendar.
+ internal static DateTime minDate = new DateTime(1900, 4, 30);
+ internal static DateTime maxDate = new DateTime((new DateTime(2077, 11, 16, 23, 59, 59, 999)).Ticks + 9999);
+
+ public override DateTime MinSupportedDateTime
+ {
+ get
+ {
+ return (minDate);
+ }
+ }
+
+ public override DateTime MaxSupportedDateTime
+ {
+ get
+ {
+ return (maxDate);
+ }
+ }
+
+ public override CalendarAlgorithmType AlgorithmType
+ {
+ get
+ {
+ return CalendarAlgorithmType.LunarCalendar;
+ }
+ }
+
+ public UmAlQuraCalendar()
+ {
+ }
+
+ internal override CalendarId BaseCalendarID
+ {
+ get
+ {
+ return (CalendarId.HIJRI);
+ }
+ }
+
+ internal override CalendarId ID
+ {
+ get
+ {
+ return (CalendarId.UMALQURA);
+ }
+ }
+
+ protected override int DaysInYearBeforeMinSupportedYear
+ {
+ get
+ {
+ // HijriCalendar has same number of days as UmAlQuraCalendar for any given year
+ // HijriCalendar says year 1317 has 355 days.
+ return 355;
+ }
+ }
+
+ /*==========================ConvertHijriToGregorian==========================
+ ** Purpose: convert Hdate(year,month,day) to Gdate(year,month,day)
+ ** Arguments:
+ ** Input/Ouput: Hijrah date: year:yh, month:mh, day:dh
+ ** Output: Gregorian date: year:yg, month:mg, day:dg , day of week:dayweek
+ ** and returns flag found:1 not found:0
+ =========================ConvertHijriToGregorian============================*/
+ private static void ConvertHijriToGregorian(int HijriYear, int HijriMonth, int HijriDay, ref int yg, ref int mg, ref int dg)
+ {
+ Debug.Assert((HijriYear >= MinCalendarYear) && (HijriYear <= MaxCalendarYear), "Hijri year is out of range.");
+ Debug.Assert(HijriMonth >= 1, "Hijri month is out of range.");
+ Debug.Assert(HijriDay >= 1, "Hijri day is out of range.");
+ int index, b, nDays = HijriDay - 1;
+ DateTime dt;
+
+
+ index = HijriYear - MinCalendarYear;
+ dt = s_hijriYearInfo[index].GregorianDate;
+
+
+ b = s_hijriYearInfo[index].HijriMonthsLengthFlags;
+
+ for (int m = 1; m < HijriMonth; m++)
+ {
+ nDays = nDays + 29 + (b & 1); /* Add the months lengths before mh */
+ b = b >> 1;
+ }
+
+ dt = dt.AddDays(nDays);
+ yg = dt.Year;
+ mg = dt.Month;
+ dg = dt.Day;
+ }
+
+ /*=================================GetAbsoluteDateUmAlQura==========================
+ **Action: Gets the Absolute date for the given UmAlQura date. The absolute date means
+ ** the number of days from January 1st, 1 A.D.
+ **Returns:
+ **Arguments:
+ **Exceptions:
+ ============================================================================*/
+ private static long GetAbsoluteDateUmAlQura(int year, int month, int day)
+ {
+ //Caller should check the validaty of year, month and day.
+
+ int yg = 0, mg = 0, dg = 0;
+ ConvertHijriToGregorian(year, month, day, ref yg, ref mg, ref dg);
+ return GregorianCalendar.GetAbsoluteDate(yg, mg, dg);
+ }
+
+ internal static void CheckTicksRange(long ticks)
+ {
+ if (ticks < minDate.Ticks || ticks > maxDate.Ticks)
+ {
+ throw new ArgumentOutOfRangeException(
+ "time",
+ String.Format(
+ CultureInfo.InvariantCulture,
+ SR.ArgumentOutOfRange_CalendarRange,
+ minDate,
+ maxDate));
+ }
+ }
+
+ internal static void CheckEraRange(int era)
+ {
+ if (era != CurrentEra && era != UmAlQuraEra)
+ {
+ throw new ArgumentOutOfRangeException(nameof(era), SR.ArgumentOutOfRange_InvalidEraValue);
+ }
+ }
+
+ internal static void CheckYearRange(int year, int era)
+ {
+ CheckEraRange(era);
+ if (year < MinCalendarYear || year > MaxCalendarYear)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ MinCalendarYear,
+ MaxCalendarYear));
+ }
+ }
+
+ internal static void CheckYearMonthRange(int year, int month, int era)
+ {
+ CheckYearRange(year, era);
+ if (month < 1 || month > 12)
+ {
+ throw new ArgumentOutOfRangeException(nameof(month), SR.ArgumentOutOfRange_Month);
+ }
+ }
+
+ /*========================ConvertGregorianToHijri============================
+ ** Purpose: convert DateTime to Hdate(year,month,day)
+ ** Arguments:
+ ** Input: DateTime
+ ** Output: Hijrah date: year:yh, month:mh, day:dh
+ ============================================================================*/
+ private static void ConvertGregorianToHijri(DateTime time, ref int HijriYear, ref int HijriMonth, ref int HijriDay)
+ {
+ int index, b, DaysPerThisMonth;
+ double nDays;
+ TimeSpan ts;
+ int yh1 = 0, mh1 = 0, dh1 = 0;
+
+ Debug.Assert((time.Ticks >= minDate.Ticks) && (time.Ticks <= maxDate.Ticks), "Gregorian date is out of range.");
+
+ // Find the index where we should start our search by quessing the Hijri year that we will be in HijriYearInfo.
+ // A Hijri year is 354 or 355 days. Use 355 days so that we will search from a lower index.
+
+ index = (int)((time.Ticks - minDate.Ticks) / Calendar.TicksPerDay) / 355;
+ do
+ {
+ } while (time.CompareTo(s_hijriYearInfo[++index].GregorianDate) > 0); //while greater
+
+ if (time.CompareTo(s_hijriYearInfo[index].GregorianDate) != 0)
+ {
+ index--;
+ }
+
+ ts = time.Subtract(s_hijriYearInfo[index].GregorianDate);
+ yh1 = index + MinCalendarYear;
+
+ mh1 = 1;
+ dh1 = 1;
+ nDays = ts.TotalDays;
+ b = s_hijriYearInfo[index].HijriMonthsLengthFlags;
+ DaysPerThisMonth = 29 + (b & 1);
+
+ while (nDays >= DaysPerThisMonth)
+ {
+ nDays -= DaysPerThisMonth;
+ b = b >> 1;
+ DaysPerThisMonth = 29 + (b & 1);
+ mh1++;
+ }
+ dh1 += (int)nDays;
+
+ HijriDay = dh1;
+ HijriMonth = mh1;
+ HijriYear = yh1;
+ }
+
+ /*=================================GetDatePart==========================
+ **Action: Returns a given date part of this <i>DateTime</i>. This method is used
+ ** to compute the year, day-of-year, month, or day part.
+ **Returns:
+ **Arguments:
+ **Exceptions: ArgumentException if part is incorrect.
+ **Notes:
+ ** First, we get the absolute date (the number of days from January 1st, 1 A.C) for the given ticks.
+ ** Use the formula (((AbsoluteDate - 226894) * 33) / (33 * 365 + 8)) + 1, we can a rough value for the UmAlQura year.
+ ** In order to get the exact UmAlQura year, we compare the exact absolute date for UmAlQuraYear and (UmAlQuraYear + 1).
+ ** From here, we can get the correct UmAlQura year.
+ ============================================================================*/
+
+ internal virtual int GetDatePart(DateTime time, int part)
+ {
+ int UmAlQuraYear = 0; // UmAlQura year
+ int UmAlQuraMonth = 0; // UmAlQura month
+ int UmAlQuraDay = 0; // UmAlQura day
+ long ticks = time.Ticks;
+ CheckTicksRange(ticks);
+
+ ConvertGregorianToHijri(time, ref UmAlQuraYear, ref UmAlQuraMonth, ref UmAlQuraDay);
+
+ if (part == DatePartYear)
+ return (UmAlQuraYear);
+
+ if (part == DatePartMonth)
+ return (UmAlQuraMonth);
+
+ if (part == DatePartDay)
+ return (UmAlQuraDay);
+
+ if (part == DatePartDayOfYear)
+ return (int)(GetAbsoluteDateUmAlQura(UmAlQuraYear, UmAlQuraMonth, UmAlQuraDay) - GetAbsoluteDateUmAlQura(UmAlQuraYear, 1, 1) + 1);
+
+ // Incorrect part value.
+ throw new InvalidOperationException(SR.InvalidOperation_DateTimeParsing);
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // months to the specified DateTime. The result is computed by incrementing
+ // (or decrementing) the year and month parts of the specified DateTime by
+ // value months, and, if required, adjusting the day part of the
+ // resulting date downwards to the last day of the resulting month in the
+ // resulting year. The time-of-day part of the result is the same as the
+ // time-of-day part of the specified DateTime.
+ //
+ // In more precise terms, considering the specified DateTime to be of the
+ // form y / m / d + t, where y is the
+ // year, m is the month, d is the day, and t is the
+ // time-of-day, the result is y1 / m1 / d1 + t,
+ // where y1 and m1 are computed by adding value months
+ // to y and m, and d1 is the largest value less than
+ // or equal to d that denotes a valid day in month m1 of year
+ // y1.
+ //
+
+
+ public override DateTime AddMonths(DateTime time, int months)
+ {
+ if (months < -120000 || months > 120000)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(months),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ -120000,
+ 120000));
+ }
+ Contract.EndContractBlock();
+ // Get the date in UmAlQura calendar.
+ int y = GetDatePart(time, DatePartYear);
+ int m = GetDatePart(time, DatePartMonth);
+ int d = GetDatePart(time, DatePartDay);
+ int i = m - 1 + months;
+
+ if (i >= 0)
+ {
+ m = i % 12 + 1;
+ y = y + i / 12;
+ }
+ else
+ {
+ m = 12 + (i + 1) % 12;
+ y = y + (i - 11) / 12;
+ }
+
+ if (d > 29)
+ {
+ int days = GetDaysInMonth(y, m);
+ if (d > days)
+ {
+ d = days;
+ }
+ }
+ CheckYearRange(y, UmAlQuraEra);
+ DateTime dt = new DateTime(GetAbsoluteDateUmAlQura(y, m, d) * TicksPerDay + time.Ticks % TicksPerDay);
+ Calendar.CheckAddResult(dt.Ticks, MinSupportedDateTime, MaxSupportedDateTime);
+ return (dt);
+ }
+
+ // Returns the DateTime resulting from adding the given number of
+ // years to the specified DateTime. The result is computed by incrementing
+ // (or decrementing) the year part of the specified DateTime by value
+ // years. If the month and day of the specified DateTime is 2/29, and if the
+ // resulting year is not a leap year, the month and day of the resulting
+ // DateTime becomes 2/28. Otherwise, the month, day, and time-of-day
+ // parts of the result are the same as those of the specified DateTime.
+ //
+
+
+ public override DateTime AddYears(DateTime time, int years)
+ {
+ return (AddMonths(time, years * 12));
+ }
+
+ // Returns the day-of-month part of the specified DateTime. The returned
+ // value is an integer between 1 and 31.
+ //
+
+
+ public override int GetDayOfMonth(DateTime time)
+ {
+ return (GetDatePart(time, DatePartDay));
+ }
+
+ // Returns the day-of-week part of the specified DateTime. The returned value
+ // is an integer between 0 and 6, where 0 indicates Sunday, 1 indicates
+ // Monday, 2 indicates Tuesday, 3 indicates Wednesday, 4 indicates
+ // Thursday, 5 indicates Friday, and 6 indicates Saturday.
+ //
+
+
+ public override DayOfWeek GetDayOfWeek(DateTime time)
+ {
+ return ((DayOfWeek)((int)(time.Ticks / TicksPerDay + 1) % 7));
+ }
+
+ // Returns the day-of-year part of the specified DateTime. The returned value
+ // is an integer between 1 and 354 or 355.
+ //
+
+
+ public override int GetDayOfYear(DateTime time)
+ {
+ return (GetDatePart(time, DatePartDayOfYear));
+ }
+
+ /*
+ internal bool CouldBeLeapYear(int year)
+ {
+ return ((((year * 11) + 14) % 30) < 11);
+ }
+ */
+
+ // Returns the number of days in the month given by the year and
+ // month arguments.
+ //
+
+
+ public override int GetDaysInMonth(int year, int month, int era)
+ {
+ CheckYearMonthRange(year, month, era);
+
+ if ((s_hijriYearInfo[year - MinCalendarYear].HijriMonthsLengthFlags & (1 << month - 1)) == 0)
+ return 29;
+ else
+ return 30;
+ }
+
+ internal static int RealGetDaysInYear(int year)
+ {
+ int days = 0, b;
+
+ Debug.Assert((year >= MinCalendarYear) && (year <= MaxCalendarYear), "Hijri year is out of range.");
+
+ b = s_hijriYearInfo[year - MinCalendarYear].HijriMonthsLengthFlags;
+
+ for (int m = 1; m <= 12; m++)
+ {
+ days = days + 29 + (b & 1); /* Add the months lengths before mh */
+ b = b >> 1;
+ }
+ Debug.Assert((days == 354) || (days == 355), "Hijri year has to be 354 or 355 days.");
+ return days;
+ }
+
+ // Returns the number of days in the year given by the year argument for the current era.
+ //
+
+
+ public override int GetDaysInYear(int year, int era)
+ {
+ CheckYearRange(year, era);
+ return (RealGetDaysInYear(year));
+ }
+
+ // Returns the era for the specified DateTime value.
+
+
+ public override int GetEra(DateTime time)
+ {
+ CheckTicksRange(time.Ticks);
+ return (UmAlQuraEra);
+ }
+
+
+
+ public override int[] Eras
+ {
+ get
+ {
+ return (new int[] { UmAlQuraEra });
+ }
+ }
+
+ // Returns the month part of the specified DateTime. The returned value is an
+ // integer between 1 and 12.
+ //
+
+
+ public override int GetMonth(DateTime time)
+ {
+ return (GetDatePart(time, DatePartMonth));
+ }
+
+ // Returns the number of months in the specified year and era.
+
+
+ public override int GetMonthsInYear(int year, int era)
+ {
+ CheckYearRange(year, era);
+ return (12);
+ }
+
+ // Returns the year part of the specified DateTime. The returned value is an
+ // integer between MinCalendarYear and MaxCalendarYear.
+ //
+
+
+ public override int GetYear(DateTime time)
+ {
+ return (GetDatePart(time, DatePartYear));
+ }
+
+ // Checks whether a given day in the specified era is a leap day. This method returns true if
+ // the date is a leap day, or false if not.
+ //
+
+
+ public override bool IsLeapDay(int year, int month, int day, int era)
+ {
+ if (day >= 1 && day <= 29)
+ {
+ CheckYearMonthRange(year, month, era);
+ return (false);
+ }
+
+ // The year/month/era value checking is done in GetDaysInMonth().
+ int daysInMonth = GetDaysInMonth(year, month, era);
+ if (day < 1 || day > daysInMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Day,
+ daysInMonth,
+ month));
+ }
+ return (false);
+ }
+
+ // Returns the leap month in a calendar year of the specified era. This method returns 0
+ // if this calendar does not have leap month, or this year is not a leap year.
+ //
+
+
+ public override int GetLeapMonth(int year, int era)
+ {
+ CheckYearRange(year, era);
+ return (0);
+ }
+
+ // Checks whether a given month in the specified era is a leap month. This method returns true if
+ // month is a leap month, or false if not.
+ //
+
+
+ public override bool IsLeapMonth(int year, int month, int era)
+ {
+ CheckYearMonthRange(year, month, era);
+ return (false);
+ }
+
+ // Checks whether a given year in the specified era is a leap year. This method returns true if
+ // year is a leap year, or false if not.
+ //
+
+
+ public override bool IsLeapYear(int year, int era)
+ {
+ CheckYearRange(year, era);
+ if (RealGetDaysInYear(year) == 355)
+ return true;
+ else
+ return false;
+ }
+
+ // Returns the date and time converted to a DateTime value. Throws an exception if the n-tuple is invalid.
+ //
+
+
+ public override DateTime ToDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int era)
+ {
+ if (day >= 1 && day <= 29)
+ {
+ CheckYearMonthRange(year, month, era);
+ goto DayInRang;
+ }
+
+ // The year/month/era value checking is done in GetDaysInMonth().
+ int daysInMonth = GetDaysInMonth(year, month, era);
+
+ if (day < 1 || day > daysInMonth)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(day),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Day,
+ daysInMonth,
+ month));
+ }
+ DayInRang:
+ long lDate = GetAbsoluteDateUmAlQura(year, month, day);
+
+ if (lDate >= 0)
+ {
+ return (new DateTime(lDate * GregorianCalendar.TicksPerDay + TimeToTicks(hour, minute, second, millisecond)));
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_BadYearMonthDay);
+ }
+ }
+
+ private const int DEFAULT_TWO_DIGIT_YEAR_MAX = 1451;
+
+
+
+ public override int TwoDigitYearMax
+ {
+ get
+ {
+ if (twoDigitYearMax == -1)
+ {
+ twoDigitYearMax = GetSystemTwoDigitYearSetting(ID, DEFAULT_TWO_DIGIT_YEAR_MAX);
+ }
+ return (twoDigitYearMax);
+ }
+
+ set
+ {
+ if (value != 99 && (value < MinCalendarYear || value > MaxCalendarYear))
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(value),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ MinCalendarYear,
+ MaxCalendarYear));
+ }
+ Contract.EndContractBlock();
+ VerifyWritable();
+ // We allow year 99 to be set so that one can make ToFourDigitYearMax a no-op by setting TwoDigitYearMax to 99.
+ twoDigitYearMax = value;
+ }
+ }
+
+
+
+ public override int ToFourDigitYear(int year)
+ {
+ if (year < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(year),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.EndContractBlock();
+
+ if (year < 100)
+ {
+ return (base.ToFourDigitYear(year));
+ }
+
+ if ((year < MinCalendarYear) || (year > MaxCalendarYear))
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(year),
+ String.Format(
+ CultureInfo.CurrentCulture,
+ SR.ArgumentOutOfRange_Range,
+ MinCalendarYear,
+ MaxCalendarYear));
+ }
+ return (year);
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Globalization/UnicodeCategory.cs b/src/mscorlib/shared/System/Globalization/UnicodeCategory.cs
new file mode 100644
index 0000000000..f0ae1fdfd9
--- /dev/null
+++ b/src/mscorlib/shared/System/Globalization/UnicodeCategory.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Globalization
+{
+ public enum UnicodeCategory
+ {
+ UppercaseLetter = 0,
+ LowercaseLetter = 1,
+ TitlecaseLetter = 2,
+ ModifierLetter = 3,
+ OtherLetter = 4,
+ NonSpacingMark = 5,
+ SpacingCombiningMark = 6,
+ EnclosingMark = 7,
+ DecimalDigitNumber = 8,
+ LetterNumber = 9,
+ OtherNumber = 10,
+ SpaceSeparator = 11,
+ LineSeparator = 12,
+ ParagraphSeparator = 13,
+ Control = 14,
+ Format = 15,
+ Surrogate = 16,
+ PrivateUse = 17,
+ ConnectorPunctuation = 18,
+ DashPunctuation = 19,
+ OpenPunctuation = 20,
+ ClosePunctuation = 21,
+ InitialQuotePunctuation = 22,
+ FinalQuotePunctuation = 23,
+ OtherPunctuation = 24,
+ MathSymbol = 25,
+ CurrencySymbol = 26,
+ ModifierSymbol = 27,
+ OtherSymbol = 28,
+ OtherNotAssigned = 29,
+ }
+}
diff --git a/src/mscorlib/shared/System/IAsyncResult.cs b/src/mscorlib/shared/System/IAsyncResult.cs
new file mode 100644
index 0000000000..0abeaca525
--- /dev/null
+++ b/src/mscorlib/shared/System/IAsyncResult.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IAsyncResult
+**
+** Purpose: Interface to encapsulate the results of an async
+** operation
+**
+===========================================================*/
+
+using System;
+using System.Threading;
+
+namespace System
+{
+ public interface IAsyncResult
+ {
+ bool IsCompleted { get; }
+
+ WaitHandle AsyncWaitHandle { get; }
+
+
+ Object AsyncState { get; }
+
+ bool CompletedSynchronously { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/ICloneable.cs b/src/mscorlib/shared/System/ICloneable.cs
new file mode 100644
index 0000000000..9f123e45c8
--- /dev/null
+++ b/src/mscorlib/shared/System/ICloneable.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ public interface ICloneable
+ {
+ object Clone();
+ }
+}
diff --git a/src/mscorlib/shared/System/IComparable.cs b/src/mscorlib/shared/System/IComparable.cs
new file mode 100644
index 0000000000..72aeeb027c
--- /dev/null
+++ b/src/mscorlib/shared/System/IComparable.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ // The IComparable interface is implemented by classes that support an
+ // ordering of instances of the class. The ordering represented by
+ // IComparable can be used to sort arrays and collections of objects
+ // that implement the interface.
+ //
+ public interface IComparable
+ {
+ // Interface does not need to be marked with the serializable attribute
+ // Compares this object to another object, returning an integer that
+ // indicates the relationship. An implementation of this method must return
+ // a value less than zero if this is less than object, zero
+ // if this is equal to object, or a value greater than zero
+ // if this is greater than object.
+ //
+ int CompareTo(Object obj);
+ }
+
+ // Generic version of IComparable.
+
+ public interface IComparable<in T>
+ {
+ // Interface does not need to be marked with the serializable attribute
+ // Compares this object to another object, returning an integer that
+ // indicates the relationship. An implementation of this method must return
+ // a value less than zero if this is less than object, zero
+ // if this is equal to object, or a value greater than zero
+ // if this is greater than object.
+ //
+ int CompareTo(T other);
+ }
+}
diff --git a/src/mscorlib/shared/System/IConvertible.cs b/src/mscorlib/shared/System/IConvertible.cs
new file mode 100644
index 0000000000..87351127f2
--- /dev/null
+++ b/src/mscorlib/shared/System/IConvertible.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ // The IConvertible interface represents an object that contains a value. This
+ // interface is implemented by the following types in the System namespace:
+ // Boolean, Char, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64,
+ // Single, Double, Decimal, DateTime, and String. The interface may
+ // be implemented by other types that are to be considered values. For example,
+ // a library of nullable database types could implement IConvertible.
+ //
+ // Note: The interface was originally proposed as IValue.
+ //
+ // The implementations of IConvertible provided by the System.XXX value classes
+ // simply forward to the appropriate Value.ToXXX(YYY) methods (a description of
+ // the Value class follows below). In cases where a Value.ToXXX(YYY) method
+ // does not exist (because the particular conversion is not supported), the
+ // IConvertible implementation should simply throw an InvalidCastException.
+
+ [CLSCompliant(false)]
+ public interface IConvertible
+ {
+ // Returns the type code of this object. An implementation of this method
+ // must not return TypeCode.Empty (which represents a null reference) or
+ // TypeCode.Object (which represents an object that doesn't implement the
+ // IConvertible interface). An implementation of this method should return
+ // TypeCode.DBNull if the value of this object is a database null. For
+ // example, a nullable integer type should return TypeCode.DBNull if the
+ // value of the object is the database null. Otherwise, an implementation
+ // of this method should return the TypeCode that best describes the
+ // internal representation of the object.
+
+ TypeCode GetTypeCode();
+
+ // The ToXXX methods convert the value of the underlying object to the
+ // given type. If a particular conversion is not supported, the
+ // implementation must throw an InvalidCastException. If the value of the
+ // underlying object is not within the range of the target type, the
+ // implementation must throw an OverflowException. The
+ // IFormatProvider will be used to get a NumberFormatInfo or similar
+ // appropriate service object, and may safely be null.
+
+ bool ToBoolean(IFormatProvider provider);
+ char ToChar(IFormatProvider provider);
+ sbyte ToSByte(IFormatProvider provider);
+ byte ToByte(IFormatProvider provider);
+ short ToInt16(IFormatProvider provider);
+ ushort ToUInt16(IFormatProvider provider);
+ int ToInt32(IFormatProvider provider);
+ uint ToUInt32(IFormatProvider provider);
+ long ToInt64(IFormatProvider provider);
+ ulong ToUInt64(IFormatProvider provider);
+ float ToSingle(IFormatProvider provider);
+ double ToDouble(IFormatProvider provider);
+ Decimal ToDecimal(IFormatProvider provider);
+ DateTime ToDateTime(IFormatProvider provider);
+ String ToString(IFormatProvider provider);
+ Object ToType(Type conversionType, IFormatProvider provider);
+ }
+}
+
diff --git a/src/mscorlib/shared/System/ICustomFormatter.cs b/src/mscorlib/shared/System/ICustomFormatter.cs
new file mode 100644
index 0000000000..47340f3093
--- /dev/null
+++ b/src/mscorlib/shared/System/ICustomFormatter.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: ICustomFormatter
+**
+**
+** Purpose: Marks a class as providing special formatting
+**
+**
+===========================================================*/
+
+using System;
+
+namespace System
+{
+ public interface ICustomFormatter
+ {
+ // Interface does not need to be marked with the serializable attribute
+ String Format(String format, Object arg, IFormatProvider formatProvider);
+ }
+}
diff --git a/src/mscorlib/shared/System/IDisposable.cs b/src/mscorlib/shared/System/IDisposable.cs
new file mode 100644
index 0000000000..24f0740edc
--- /dev/null
+++ b/src/mscorlib/shared/System/IDisposable.cs
@@ -0,0 +1,60 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Interface: IDisposable
+**
+**
+** Purpose: Interface for assisting with deterministic finalization.
+**
+**
+===========================================================*/
+
+namespace System
+{
+ // IDisposable is an attempt at helping to solve problems with deterministic
+ // finalization. The GC of course doesn't leave any way to deterministically
+ // know when a finalizer will run. This forces classes that hold onto OS
+ // resources or some sort of important state (such as a FileStream or a
+ // network connection) to provide a Close or Dispose method so users can
+ // run clean up code deterministically. We have formalized this into an
+ // interface with one method. Classes may privately implement IDisposable and
+ // provide a Close method instead, if that name is by far the expected name
+ // for objects in that domain (ie, you don't Dispose of a FileStream, you Close
+ // it).
+ //
+ // This interface could be theoretically used as a marker by a compiler to
+ // ensure a disposable object has been cleaned up along all code paths if it's
+ // been allocated in that method, though in practice any compiler that
+ // draconian may tick off any number of people. Perhaps an external tool (like
+ // like Purify or BoundsChecker) could do this. Instead, C# has added a using
+ // clause, which will generate a try/finally statement where the resource
+ // passed into the using clause will always have it's Dispose method called.
+ // Syntax is using(FileStream fs = ...) { .. };
+ //
+ // Dispose should meet the following conditions:
+ // 1) Be safely callable multiple times
+ // 2) Release any resources associated with the instance
+ // 3) Call the base class's Dispose method, if necessary
+ // 4) Suppress finalization of this class to help the GC by reducing the
+ // number of objects on the finalization queue.
+ // 5) Dispose shouldn't generally throw exceptions, except for very serious
+ // errors that are particularly unexpected. (ie, OutOfMemoryException)
+ // Ideally, nothing should go wrong with your object by calling Dispose.
+ //
+ // If possible, a class should define a finalizer that calls Dispose.
+ // However, in many situations, this is impractical. For instance, take the
+ // classic example of a Stream and a StreamWriter (which has an internal
+ // buffer of data to write to the Stream). If both objects are collected
+ // before Close or Dispose has been called on either, then the GC may run the
+ // finalizer for the Stream first, before the StreamWriter. At that point, any
+ // data buffered by the StreamWriter cannot be written to the Stream. In this
+ // case, it doesn't make much sense to provide a finalizer on the StreamWriter
+ // since you cannot solve this problem correctly.
+ public interface IDisposable
+ {
+ void Dispose();
+ }
+}
diff --git a/src/mscorlib/shared/System/IEquatable.cs b/src/mscorlib/shared/System/IEquatable.cs
new file mode 100644
index 0000000000..1264fdd57a
--- /dev/null
+++ b/src/mscorlib/shared/System/IEquatable.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System
+{
+ public interface IEquatable<T>
+ {
+ bool Equals(T other);
+ }
+}
+
diff --git a/src/mscorlib/shared/System/IFormatProvider.cs b/src/mscorlib/shared/System/IFormatProvider.cs
new file mode 100644
index 0000000000..0c17354af3
--- /dev/null
+++ b/src/mscorlib/shared/System/IFormatProvider.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Notes a class which knows how to return formatting information
+**
+**
+============================================================*/
+
+using System;
+
+namespace System
+{
+ public interface IFormatProvider
+ {
+ // Interface does not need to be marked with the serializable attribute
+ Object GetFormat(Type formatType);
+ }
+}
diff --git a/src/mscorlib/shared/System/IFormattable.cs b/src/mscorlib/shared/System/IFormattable.cs
new file mode 100644
index 0000000000..28a7d70571
--- /dev/null
+++ b/src/mscorlib/shared/System/IFormattable.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+
+namespace System
+{
+ public interface IFormattable
+ {
+ [Pure]
+ String ToString(String format, IFormatProvider formatProvider);
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs b/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs
new file mode 100644
index 0000000000..786c2106a3
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/DirectoryNotFoundException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.IO
+{
+ /*
+ * Thrown when trying to access a directory that doesn't exist on disk.
+ * From COM Interop, this exception is thrown for 2 HRESULTS:
+ * the Win32 errorcode-as-HRESULT ERROR_PATH_NOT_FOUND (0x80070003)
+ * and STG_E_PATHNOTFOUND (0x80030003).
+ */
+ [Serializable]
+ public class DirectoryNotFoundException : IOException
+ {
+ public DirectoryNotFoundException()
+ : base(SR.Arg_DirectoryNotFoundException)
+ {
+ HResult = __HResults.COR_E_DIRECTORYNOTFOUND;
+ }
+
+ public DirectoryNotFoundException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_DIRECTORYNOTFOUND;
+ }
+
+ public DirectoryNotFoundException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_DIRECTORYNOTFOUND;
+ }
+
+ protected DirectoryNotFoundException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/EndOfStreamException.cs b/src/mscorlib/shared/System/IO/EndOfStreamException.cs
new file mode 100644
index 0000000000..7c4b2b744f
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/EndOfStreamException.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.IO
+{
+ [Serializable]
+ public class EndOfStreamException : IOException
+ {
+ public EndOfStreamException()
+ : base(SR.Arg_EndOfStreamException)
+ {
+ HResult = __HResults.COR_E_ENDOFSTREAM;
+ }
+
+ public EndOfStreamException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_ENDOFSTREAM;
+ }
+
+ public EndOfStreamException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_ENDOFSTREAM;
+ }
+
+ protected EndOfStreamException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/Error.cs b/src/mscorlib/shared/System/IO/Error.cs
new file mode 100644
index 0000000000..2aef895181
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/Error.cs
@@ -0,0 +1,49 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.InteropServices;
+using System.Text;
+using System.Globalization;
+using System.Diagnostics.Contracts;
+
+namespace System.IO
+{
+ /// <summary>
+ /// Provides centralized methods for creating exceptions for System.IO.FileSystem.
+ /// </summary>
+ [Pure]
+ internal static class Error
+ {
+ internal static Exception GetStreamIsClosed()
+ {
+ return new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed);
+ }
+
+ internal static Exception GetEndOfFile()
+ {
+ return new EndOfStreamException(SR.IO_EOF_ReadBeyondEOF);
+ }
+
+ internal static Exception GetFileNotOpen()
+ {
+ return new ObjectDisposedException(null, SR.ObjectDisposed_FileClosed);
+ }
+
+ internal static Exception GetReadNotSupported()
+ {
+ return new NotSupportedException(SR.NotSupported_UnreadableStream);
+ }
+
+ internal static Exception GetSeekNotSupported()
+ {
+ return new NotSupportedException(SR.NotSupported_UnseekableStream);
+ }
+
+ internal static Exception GetWriteNotSupported()
+ {
+ return new NotSupportedException(SR.NotSupported_UnwritableStream);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileAccess.cs b/src/mscorlib/shared/System/IO/FileAccess.cs
new file mode 100644
index 0000000000..c6e583b34a
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileAccess.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.IO
+{
+ // Contains constants for specifying the access you want for a file.
+ // You can have Read, Write or ReadWrite access.
+ //
+ [Serializable]
+ [Flags]
+ public enum FileAccess
+ {
+ // Specifies read access to the file. Data can be read from the file and
+ // the file pointer can be moved. Combine with WRITE for read-write access.
+ Read = 1,
+
+ // Specifies write access to the file. Data can be written to the file and
+ // the file pointer can be moved. Combine with READ for read-write access.
+ Write = 2,
+
+ // Specifies read and write access to the file. Data can be written to the
+ // file and the file pointer can be moved. Data can also be read from the
+ // file.
+ ReadWrite = 3,
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileLoadException.cs b/src/mscorlib/shared/System/IO/FileLoadException.cs
new file mode 100644
index 0000000000..b5e197c143
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileLoadException.cs
@@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.IO
+{
+ [Serializable]
+ public partial class FileLoadException : IOException
+ {
+ public FileLoadException()
+ : base(SR.IO_FileLoad)
+ {
+ HResult = __HResults.COR_E_FILELOAD;
+ }
+
+ public FileLoadException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_FILELOAD;
+ }
+
+ public FileLoadException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_FILELOAD;
+ }
+
+ public FileLoadException(string message, string fileName) : base(message)
+ {
+ HResult = __HResults.COR_E_FILELOAD;
+ FileName = fileName;
+ }
+
+ public FileLoadException(string message, string fileName, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_FILELOAD;
+ FileName = fileName;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ if (_message == null)
+ {
+ _message = FormatFileLoadExceptionMessage(FileName, HResult);
+ }
+ return _message;
+ }
+ }
+
+ public string FileName { get; }
+ public string FusionLog { get; }
+
+ public override string ToString()
+ {
+ string s = GetType().ToString() + ": " + Message;
+
+ if (FileName != null && FileName.Length != 0)
+ s += Environment.NewLine + SR.Format(SR.IO_FileName_Name, FileName);
+
+ if (InnerException != null)
+ s = s + " ---> " + InnerException.ToString();
+
+ if (StackTrace != null)
+ s += Environment.NewLine + StackTrace;
+
+ if (FusionLog != null)
+ {
+ if (s == null)
+ s = " ";
+ s += Environment.NewLine;
+ s += Environment.NewLine;
+ s += FusionLog;
+ }
+
+ return s;
+ }
+
+ protected FileLoadException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ // Base class constructor will check info != null.
+
+ FileName = info.GetString("FileLoad_FileName");
+ FusionLog = info.GetString("FileLoad_FusionLog");
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ // Serialize data for our base classes. base will verify info != null.
+ base.GetObjectData(info, context);
+
+ // Serialize data for this class
+ info.AddValue("FileLoad_FileName", FileName, typeof(string));
+ info.AddValue("FileLoad_FusionLog", FusionLog, typeof(string));
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileMode.cs b/src/mscorlib/shared/System/IO/FileMode.cs
new file mode 100644
index 0000000000..77f2fe6f20
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileMode.cs
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.IO
+{
+ // Contains constants for specifying how the OS should open a file.
+ // These will control whether you overwrite a file, open an existing
+ // file, or some combination thereof.
+ //
+ // To append to a file, use Append (which maps to OpenOrCreate then we seek
+ // to the end of the file). To truncate a file or create it if it doesn't
+ // exist, use Create.
+ //
+ public enum FileMode
+ {
+ // Creates a new file. An exception is raised if the file already exists.
+ CreateNew = 1,
+
+ // Creates a new file. If the file already exists, it is overwritten.
+ Create = 2,
+
+ // Opens an existing file. An exception is raised if the file does not exist.
+ Open = 3,
+
+ // Opens the file if it exists. Otherwise, creates a new file.
+ OpenOrCreate = 4,
+
+ // Opens an existing file. Once opened, the file is truncated so that its
+ // size is zero bytes. The calling process must open the file with at least
+ // WRITE access. An exception is raised if the file does not exist.
+ Truncate = 5,
+
+ // Opens the file if it exists and seeks to the end. Otherwise,
+ // creates a new file.
+ Append = 6,
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileNotFoundException.cs b/src/mscorlib/shared/System/IO/FileNotFoundException.cs
new file mode 100644
index 0000000000..5d86b8f635
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileNotFoundException.cs
@@ -0,0 +1,114 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.IO
+{
+ // Thrown when trying to access a file that doesn't exist on disk.
+ [Serializable]
+ public partial class FileNotFoundException : IOException
+ {
+ public FileNotFoundException()
+ : base(SR.IO_FileNotFound)
+ {
+ HResult = __HResults.COR_E_FILENOTFOUND;
+ }
+
+ public FileNotFoundException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_FILENOTFOUND;
+ }
+
+ public FileNotFoundException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_FILENOTFOUND;
+ }
+
+ public FileNotFoundException(string message, string fileName)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_FILENOTFOUND;
+ FileName = fileName;
+ }
+
+ public FileNotFoundException(string message, string fileName, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_FILENOTFOUND;
+ FileName = fileName;
+ }
+
+ public override string Message
+ {
+ get
+ {
+ SetMessageField();
+ return _message;
+ }
+ }
+
+ private void SetMessageField()
+ {
+ if (_message == null)
+ {
+ if ((FileName == null) &&
+ (HResult == System.__HResults.COR_E_EXCEPTION))
+ _message = SR.IO_FileNotFound;
+
+ else if (FileName != null)
+ _message = FileLoadException.FormatFileLoadExceptionMessage(FileName, HResult);
+ }
+ }
+
+ public string FileName { get; }
+ public string FusionLog { get; }
+
+ public override string ToString()
+ {
+ string s = GetType().ToString() + ": " + Message;
+
+ if (FileName != null && FileName.Length != 0)
+ s += Environment.NewLine + SR.Format(SR.IO_FileName_Name, FileName);
+
+ if (InnerException != null)
+ s = s + " ---> " + InnerException.ToString();
+
+ if (StackTrace != null)
+ s += Environment.NewLine + StackTrace;
+
+ if (FusionLog != null)
+ {
+ if (s == null)
+ s = " ";
+ s += Environment.NewLine;
+ s += Environment.NewLine;
+ s += FusionLog;
+ }
+ return s;
+ }
+
+ protected FileNotFoundException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ // Base class constructor will check info != null.
+
+ FileName = info.GetString("FileNotFound_FileName");
+ FusionLog = info.GetString("FileNotFound_FusionLog");
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ // Serialize data for our base classes. base will verify info != null.
+ base.GetObjectData(info, context);
+
+ // Serialize data for this class
+ info.AddValue("FileNotFound_FileName", FileName, typeof(string));
+ info.AddValue("FileNotFound_FusionLog", FusionLog, typeof(string));
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/IO/FileOptions.cs b/src/mscorlib/shared/System/IO/FileOptions.cs
new file mode 100644
index 0000000000..ae8396a588
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileOptions.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.InteropServices;
+
+namespace System.IO
+{
+ // Maps to FILE_FLAG_DELETE_ON_CLOSE and similar values from winbase.h.
+ // We didn't expose a number of these values because we didn't believe
+ // a number of them made sense in managed code, at least not yet.
+ [Flags]
+ public enum FileOptions
+ {
+ // NOTE: any change to FileOptions enum needs to be
+ // matched in the FileStream ctor for error validation
+ None = 0,
+ WriteThrough = unchecked((int)0x80000000),
+ Asynchronous = unchecked((int)0x40000000), // FILE_FLAG_OVERLAPPED
+ // NoBuffering = 0x20000000,
+ RandomAccess = 0x10000000,
+ DeleteOnClose = 0x04000000,
+ SequentialScan = 0x08000000,
+ // AllowPosix = 0x01000000, // FILE_FLAG_POSIX_SEMANTICS
+ // BackupOrRestore,
+ // DisallowReparsePoint = 0x00200000, // FILE_FLAG_OPEN_REPARSE_POINT
+ // NoRemoteRecall = 0x00100000, // FILE_FLAG_OPEN_NO_RECALL
+ // FirstPipeInstance = 0x00080000, // FILE_FLAG_FIRST_PIPE_INSTANCE
+ Encrypted = 0x00004000, // FILE_ATTRIBUTE_ENCRYPTED
+ }
+}
+
diff --git a/src/mscorlib/shared/System/IO/FileShare.cs b/src/mscorlib/shared/System/IO/FileShare.cs
new file mode 100644
index 0000000000..e9b9b5e32f
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileShare.cs
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.IO
+{
+ // Contains constants for controlling file sharing options while
+ // opening files. You can specify what access other processes trying
+ // to open the same file concurrently can have.
+ //
+ // Note these values currently match the values for FILE_SHARE_READ,
+ // FILE_SHARE_WRITE, and FILE_SHARE_DELETE in winnt.h
+ //
+ [Flags]
+ public enum FileShare
+ {
+ // No sharing. Any request to open the file (by this process or another
+ // process) will fail until the file is closed.
+ None = 0,
+
+ // Allows subsequent opening of the file for reading. If this flag is not
+ // specified, any request to open the file for reading (by this process or
+ // another process) will fail until the file is closed.
+ Read = 1,
+
+ // Allows subsequent opening of the file for writing. If this flag is not
+ // specified, any request to open the file for writing (by this process or
+ // another process) will fail until the file is closed.
+ Write = 2,
+
+ // Allows subsequent opening of the file for writing or reading. If this flag
+ // is not specified, any request to open the file for writing or reading (by
+ // this process or another process) will fail until the file is closed.
+ ReadWrite = 3,
+
+ // Open the file, but allow someone else to delete the file.
+ Delete = 4,
+
+ // Whether the file handle should be inheritable by child processes.
+ // Note this is not directly supported like this by Win32.
+ Inheritable = 0x10,
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileStream.Linux.cs b/src/mscorlib/shared/System/IO/FileStream.Linux.cs
new file mode 100644
index 0000000000..873c4eb559
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileStream.Linux.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32.SafeHandles;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.IO
+{
+ public partial class FileStream : Stream
+ {
+ /// <summary>Prevents other processes from reading from or writing to the FileStream.</summary>
+ /// <param name="position">The beginning of the range to lock.</param>
+ /// <param name="length">The range to be locked.</param>
+ private void LockInternal(long position, long length)
+ {
+ CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, Interop.Sys.LockType.F_WRLCK));
+ }
+
+ /// <summary>Allows access by other processes to all or part of a file that was previously locked.</summary>
+ /// <param name="position">The beginning of the range to unlock.</param>
+ /// <param name="length">The range to be unlocked.</param>
+ private void UnlockInternal(long position, long length)
+ {
+ CheckFileCall(Interop.Sys.LockFileRegion(_fileHandle, position, length, Interop.Sys.LockType.F_UNLCK));
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileStream.OSX.cs b/src/mscorlib/shared/System/IO/FileStream.OSX.cs
new file mode 100644
index 0000000000..f29e922337
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileStream.OSX.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.IO
+{
+ public partial class FileStream : Stream
+ {
+ private void LockInternal(long position, long length)
+ {
+ throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking);
+ }
+
+ private void UnlockInternal(long position, long length)
+ {
+ throw new PlatformNotSupportedException(SR.PlatformNotSupported_OSXFileLocking);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileStream.Unix.cs b/src/mscorlib/shared/System/IO/FileStream.Unix.cs
new file mode 100644
index 0000000000..7d860ac2fe
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileStream.Unix.cs
@@ -0,0 +1,933 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32.SafeHandles;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.IO
+{
+ /// <summary>Provides an implementation of a file stream for Unix files.</summary>
+ public partial class FileStream : Stream
+ {
+ /// <summary>File mode.</summary>
+ private FileMode _mode;
+
+ /// <summary>Advanced options requested when opening the file.</summary>
+ private FileOptions _options;
+
+ /// <summary>If the file was opened with FileMode.Append, the length of the file when opened; otherwise, -1.</summary>
+ private long _appendStart = -1;
+
+ /// <summary>
+ /// Extra state used by the file stream when _useAsyncIO is true. This includes
+ /// the semaphore used to serialize all operation, the buffer/offset/count provided by the
+ /// caller for ReadAsync/WriteAsync operations, and the last successful task returned
+ /// synchronously from ReadAsync which can be reused if the count matches the next request.
+ /// Only initialized when <see cref="_useAsyncIO"/> is true.
+ /// </summary>
+ private AsyncState _asyncState;
+
+ /// <summary>Lazily-initialized value for whether the file supports seeking.</summary>
+ private bool? _canSeek;
+
+ private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options)
+ {
+ // FileStream performs most of the general argument validation. We can assume here that the arguments
+ // are all checked and consistent (e.g. non-null-or-empty path; valid enums in mode, access, share, and options; etc.)
+ // Store the arguments
+ _mode = mode;
+ _options = options;
+
+ if (_useAsyncIO)
+ _asyncState = new AsyncState();
+
+ // Translate the arguments into arguments for an open call.
+ Interop.Sys.OpenFlags openFlags = PreOpenConfigurationFromOptions(mode, _access, share, options);
+
+ // If the file gets created a new, we'll select the permissions for it. Most Unix utilities by default use 666 (read and
+ // write for all), so we do the same (even though this doesn't match Windows, where by default it's possible to write out
+ // a file and then execute it). No matter what we choose, it'll be subject to the umask applied by the system, such that the
+ // actual permissions will typically be less than what we select here.
+ const Interop.Sys.Permissions OpenPermissions =
+ Interop.Sys.Permissions.S_IRUSR | Interop.Sys.Permissions.S_IWUSR |
+ Interop.Sys.Permissions.S_IRGRP | Interop.Sys.Permissions.S_IWGRP |
+ Interop.Sys.Permissions.S_IROTH | Interop.Sys.Permissions.S_IWOTH;
+
+ // Open the file and store the safe handle.
+ return SafeFileHandle.Open(_path, openFlags, (int)OpenPermissions);
+ }
+
+ /// <summary>Initializes a stream for reading or writing a Unix file.</summary>
+ /// <param name="mode">How the file should be opened.</param>
+ /// <param name="share">What other access to the file should be allowed. This is currently ignored.</param>
+ private void Init(FileMode mode, FileShare share)
+ {
+ _fileHandle.IsAsync = _useAsyncIO;
+
+ // Lock the file if requested via FileShare. This is only advisory locking. FileShare.None implies an exclusive
+ // lock on the file and all other modes use a shared lock. While this is not as granular as Windows, not mandatory,
+ // and not atomic with file opening, it's better than nothing.
+ Interop.Sys.LockOperations lockOperation = (share == FileShare.None) ? Interop.Sys.LockOperations.LOCK_EX : Interop.Sys.LockOperations.LOCK_SH;
+ if (Interop.Sys.FLock(_fileHandle, lockOperation | Interop.Sys.LockOperations.LOCK_NB) < 0)
+ {
+ // The only error we care about is EWOULDBLOCK, which indicates that the file is currently locked by someone
+ // else and we would block trying to access it. Other errors, such as ENOTSUP (locking isn't supported) or
+ // EACCES (the file system doesn't allow us to lock), will only hamper FileStream's usage without providing value,
+ // given again that this is only advisory / best-effort.
+ Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
+ if (errorInfo.Error == Interop.Error.EWOULDBLOCK)
+ {
+ throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false);
+ }
+ }
+
+ // These provide hints around how the file will be accessed. Specifying both RandomAccess
+ // and Sequential together doesn't make sense as they are two competing options on the same spectrum,
+ // so if both are specified, we prefer RandomAccess (behavior on Windows is unspecified if both are provided).
+ Interop.Sys.FileAdvice fadv =
+ (_options & FileOptions.RandomAccess) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_RANDOM :
+ (_options & FileOptions.SequentialScan) != 0 ? Interop.Sys.FileAdvice.POSIX_FADV_SEQUENTIAL :
+ 0;
+ if (fadv != 0)
+ {
+ CheckFileCall(Interop.Sys.PosixFAdvise(_fileHandle, 0, 0, fadv),
+ ignoreNotSupported: true); // just a hint.
+ }
+
+ // Jump to the end of the file if opened as Append.
+ if (_mode == FileMode.Append)
+ {
+ _appendStart = SeekCore(0, SeekOrigin.End);
+ }
+ }
+
+ /// <summary>Initializes a stream from an already open file handle (file descriptor).</summary>
+ /// <param name="handle">The handle to the file.</param>
+ /// <param name="bufferSize">The size of the buffer to use when buffering.</param>
+ /// <param name="useAsyncIO">Whether access to the stream is performed asynchronously.</param>
+ private void InitFromHandle(SafeFileHandle handle)
+ {
+ if (_useAsyncIO)
+ _asyncState = new AsyncState();
+
+ if (CanSeekCore) // use non-virtual CanSeekCore rather than CanSeek to avoid making virtual call during ctor
+ SeekCore(0, SeekOrigin.Current);
+ }
+
+ /// <summary>Translates the FileMode, FileAccess, and FileOptions values into flags to be passed when opening the file.</summary>
+ /// <param name="mode">The FileMode provided to the stream's constructor.</param>
+ /// <param name="access">The FileAccess provided to the stream's constructor</param>
+ /// <param name="share">The FileShare provided to the stream's constructor</param>
+ /// <param name="options">The FileOptions provided to the stream's constructor</param>
+ /// <returns>The flags value to be passed to the open system call.</returns>
+ private static Interop.Sys.OpenFlags PreOpenConfigurationFromOptions(FileMode mode, FileAccess access, FileShare share, FileOptions options)
+ {
+ // Translate FileMode. Most of the values map cleanly to one or more options for open.
+ Interop.Sys.OpenFlags flags = default(Interop.Sys.OpenFlags);
+ switch (mode)
+ {
+ default:
+ case FileMode.Open: // Open maps to the default behavior for open(...). No flags needed.
+ break;
+
+ case FileMode.Append: // Append is the same as OpenOrCreate, except that we'll also separately jump to the end later
+ case FileMode.OpenOrCreate:
+ flags |= Interop.Sys.OpenFlags.O_CREAT;
+ break;
+
+ case FileMode.Create:
+ flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_TRUNC);
+ break;
+
+ case FileMode.CreateNew:
+ flags |= (Interop.Sys.OpenFlags.O_CREAT | Interop.Sys.OpenFlags.O_EXCL);
+ break;
+
+ case FileMode.Truncate:
+ flags |= Interop.Sys.OpenFlags.O_TRUNC;
+ break;
+ }
+
+ // Translate FileAccess. All possible values map cleanly to corresponding values for open.
+ switch (access)
+ {
+ case FileAccess.Read:
+ flags |= Interop.Sys.OpenFlags.O_RDONLY;
+ break;
+
+ case FileAccess.ReadWrite:
+ flags |= Interop.Sys.OpenFlags.O_RDWR;
+ break;
+
+ case FileAccess.Write:
+ flags |= Interop.Sys.OpenFlags.O_WRONLY;
+ break;
+ }
+
+ // Handle Inheritable, other FileShare flags are handled by Init
+ if ((share & FileShare.Inheritable) == 0)
+ {
+ flags |= Interop.Sys.OpenFlags.O_CLOEXEC;
+ }
+
+ // Translate some FileOptions; some just aren't supported, and others will be handled after calling open.
+ // - Asynchronous: Handled in ctor, setting _useAsync and SafeFileHandle.IsAsync to true
+ // - DeleteOnClose: Doesn't have a Unix equivalent, but we approximate it in Dispose
+ // - Encrypted: No equivalent on Unix and is ignored
+ // - RandomAccess: Implemented after open if posix_fadvise is available
+ // - SequentialScan: Implemented after open if posix_fadvise is available
+ // - WriteThrough: Handled here
+ if ((options & FileOptions.WriteThrough) != 0)
+ {
+ flags |= Interop.Sys.OpenFlags.O_SYNC;
+ }
+
+ return flags;
+ }
+
+ /// <summary>Gets a value indicating whether the current stream supports seeking.</summary>
+ public override bool CanSeek => CanSeekCore;
+
+ /// <summary>Gets a value indicating whether the current stream supports seeking.</summary>
+ /// <remarks>Separated out of CanSeek to enable making non-virtual call to this logic.</remarks>
+ private bool CanSeekCore
+ {
+ get
+ {
+ if (_fileHandle.IsClosed)
+ {
+ return false;
+ }
+
+ if (!_canSeek.HasValue)
+ {
+ // Lazily-initialize whether we're able to seek, tested by seeking to our current location.
+ _canSeek = Interop.Sys.LSeek(_fileHandle, 0, Interop.Sys.SeekWhence.SEEK_CUR) >= 0;
+ }
+ return _canSeek.Value;
+ }
+ }
+
+ private long GetLengthInternal()
+ {
+ // Get the length of the file as reported by the OS
+ Interop.Sys.FileStatus status;
+ CheckFileCall(Interop.Sys.FStat(_fileHandle, out status));
+ long length = status.Size;
+
+ // But we may have buffered some data to be written that puts our length
+ // beyond what the OS is aware of. Update accordingly.
+ if (_writePos > 0 && _filePosition + _writePos > length)
+ {
+ length = _writePos + _filePosition;
+ }
+
+ return length;
+ }
+
+ /// <summary>Releases the unmanaged resources used by the stream.</summary>
+ /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (_fileHandle != null && !_fileHandle.IsClosed)
+ {
+ // Flush any remaining data in the file
+ try
+ {
+ FlushWriteBuffer();
+ }
+ catch (IOException) when (!disposing)
+ {
+ // On finalization, ignore failures from trying to flush the write buffer,
+ // e.g. if this stream is wrapping a pipe and the pipe is now broken.
+ }
+
+ // If DeleteOnClose was requested when constructed, delete the file now.
+ // (Unix doesn't directly support DeleteOnClose, so we mimic it here.)
+ if (_path != null && (_options & FileOptions.DeleteOnClose) != 0)
+ {
+ // Since we still have the file open, this will end up deleting
+ // it (assuming we're the only link to it) once it's closed, but the
+ // name will be removed immediately.
+ Interop.Sys.Unlink(_path); // ignore errors; it's valid that the path may no longer exist
+ }
+ }
+ }
+ finally
+ {
+ if (_fileHandle != null && !_fileHandle.IsClosed)
+ {
+ _fileHandle.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+ }
+
+ /// <summary>Flushes the OS buffer. This does not flush the internal read/write buffer.</summary>
+ private void FlushOSBuffer()
+ {
+ if (Interop.Sys.FSync(_fileHandle) < 0)
+ {
+ Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
+ switch (errorInfo.Error)
+ {
+ case Interop.Error.EROFS:
+ case Interop.Error.EINVAL:
+ case Interop.Error.ENOTSUP:
+ // Ignore failures due to the FileStream being bound to a special file that
+ // doesn't support synchronization. In such cases there's nothing to flush.
+ break;
+ default:
+ throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false);
+ }
+ }
+ }
+
+ /// <summary>Writes any data in the write buffer to the underlying stream and resets the buffer.</summary>
+ private void FlushWriteBuffer()
+ {
+ AssertBufferInvariants();
+ if (_writePos > 0)
+ {
+ WriteNative(GetBuffer(), 0, _writePos);
+ _writePos = 0;
+ }
+ }
+
+ /// <summary>Asynchronously clears all buffers for this stream, causing any buffered data to be written to the underlying device.</summary>
+ /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+ /// <returns>A task that represents the asynchronous flush operation.</returns>
+ private Task FlushAsyncInternal(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ if (_fileHandle.IsClosed)
+ {
+ throw Error.GetFileNotOpen();
+ }
+
+ // As with Win32FileStream, flush the buffers synchronously to avoid race conditions.
+ try
+ {
+ FlushInternalBuffer();
+ }
+ catch (Exception e)
+ {
+ return Task.FromException(e);
+ }
+
+ // We then separately flush to disk asynchronously. This is only
+ // necessary if we support writing; otherwise, we're done.
+ if (CanWrite)
+ {
+ return Task.Factory.StartNew(
+ state => ((FileStream)state).FlushOSBuffer(),
+ this,
+ cancellationToken,
+ TaskCreationOptions.DenyChildAttach,
+ TaskScheduler.Default);
+ }
+ else
+ {
+ return Task.CompletedTask;
+ }
+ }
+
+ /// <summary>Sets the length of this stream to the given value.</summary>
+ /// <param name="value">The new length of the stream.</param>
+ private void SetLengthInternal(long value)
+ {
+ FlushInternalBuffer();
+
+ if (_appendStart != -1 && value < _appendStart)
+ {
+ throw new IOException(SR.IO_SetLengthAppendTruncate);
+ }
+
+ long origPos = _filePosition;
+
+ VerifyOSHandlePosition();
+
+ if (_filePosition != value)
+ {
+ SeekCore(value, SeekOrigin.Begin);
+ }
+
+ CheckFileCall(Interop.Sys.FTruncate(_fileHandle, value));
+
+ // Return file pointer to where it was before setting length
+ if (origPos != value)
+ {
+ if (origPos < value)
+ {
+ SeekCore(origPos, SeekOrigin.Begin);
+ }
+ else
+ {
+ SeekCore(0, SeekOrigin.End);
+ }
+ }
+ }
+
+ /// <summary>Reads a block of bytes from the stream and writes the data in a given buffer.</summary>
+ /// <param name="array">
+ /// When this method returns, contains the specified byte array with the values between offset and
+ /// (offset + count - 1) replaced by the bytes read from the current source.
+ /// </param>
+ /// <param name="offset">The byte offset in array at which the read bytes will be placed.</param>
+ /// <param name="count">The maximum number of bytes to read. </param>
+ /// <returns>
+ /// The total number of bytes read into the buffer. This might be less than the number of bytes requested
+ /// if that number of bytes are not currently available, or zero if the end of the stream is reached.
+ /// </returns>
+ public override int Read(byte[] array, int offset, int count)
+ {
+ ValidateReadWriteArgs(array, offset, count);
+
+ if (_useAsyncIO)
+ {
+ _asyncState.Wait();
+ try { return ReadCore(array, offset, count); }
+ finally { _asyncState.Release(); }
+ }
+ else
+ {
+ return ReadCore(array, offset, count);
+ }
+ }
+
+ /// <summary>Reads a block of bytes from the stream and writes the data in a given buffer.</summary>
+ /// <param name="array">
+ /// When this method returns, contains the specified byte array with the values between offset and
+ /// (offset + count - 1) replaced by the bytes read from the current source.
+ /// </param>
+ /// <param name="offset">The byte offset in array at which the read bytes will be placed.</param>
+ /// <param name="count">The maximum number of bytes to read. </param>
+ /// <returns>
+ /// The total number of bytes read into the buffer. This might be less than the number of bytes requested
+ /// if that number of bytes are not currently available, or zero if the end of the stream is reached.
+ /// </returns>
+ private int ReadCore(byte[] array, int offset, int count)
+ {
+ PrepareForReading();
+
+ // Are there any bytes available in the read buffer? If yes,
+ // we can just return from the buffer. If the buffer is empty
+ // or has no more available data in it, we can either refill it
+ // (and then read from the buffer into the user's buffer) or
+ // we can just go directly into the user's buffer, if they asked
+ // for more data than we'd otherwise buffer.
+ int numBytesAvailable = _readLength - _readPos;
+ bool readFromOS = false;
+ if (numBytesAvailable == 0)
+ {
+ // If we're not able to seek, then we're not able to rewind the stream (i.e. flushing
+ // a read buffer), in which case we don't want to use a read buffer. Similarly, if
+ // the user has asked for more data than we can buffer, we also want to skip the buffer.
+ if (!CanSeek || (count >= _bufferLength))
+ {
+ // Read directly into the user's buffer
+ _readPos = _readLength = 0;
+ return ReadNative(array, offset, count);
+ }
+ else
+ {
+ // Read into our buffer.
+ _readLength = numBytesAvailable = ReadNative(GetBuffer(), 0, _bufferLength);
+ _readPos = 0;
+ if (numBytesAvailable == 0)
+ {
+ return 0;
+ }
+
+ // Note that we did an OS read as part of this Read, so that later
+ // we don't try to do one again if what's in the buffer doesn't
+ // meet the user's request.
+ readFromOS = true;
+ }
+ }
+
+ // Now that we know there's data in the buffer, read from it into the user's buffer.
+ Debug.Assert(numBytesAvailable > 0, "Data must be in the buffer to be here");
+ int bytesRead = Math.Min(numBytesAvailable, count);
+ Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, bytesRead);
+ _readPos += bytesRead;
+
+ // We may not have had enough data in the buffer to completely satisfy the user's request.
+ // While Read doesn't require that we return as much data as the user requested (any amount
+ // up to the requested count is fine), FileStream on Windows tries to do so by doing a
+ // subsequent read from the file if we tried to satisfy the request with what was in the
+ // buffer but the buffer contained less than the requested count. To be consistent with that
+ // behavior, we do the same thing here on Unix. Note that we may still get less the requested
+ // amount, as the OS may give us back fewer than we request, either due to reaching the end of
+ // file, or due to its own whims.
+ if (!readFromOS && bytesRead < count)
+ {
+ Debug.Assert(_readPos == _readLength, "bytesToRead should only be < count if numBytesAvailable < count");
+ _readPos = _readLength = 0; // no data left in the read buffer
+ bytesRead += ReadNative(array, offset + bytesRead, count - bytesRead);
+ }
+
+ return bytesRead;
+ }
+
+ /// <summary>Unbuffered, reads a block of bytes from the stream and writes the data in a given buffer.</summary>
+ /// <param name="array">
+ /// When this method returns, contains the specified byte array with the values between offset and
+ /// (offset + count - 1) replaced by the bytes read from the current source.
+ /// </param>
+ /// <param name="offset">The byte offset in array at which the read bytes will be placed.</param>
+ /// <param name="count">The maximum number of bytes to read. </param>
+ /// <returns>
+ /// The total number of bytes read into the buffer. This might be less than the number of bytes requested
+ /// if that number of bytes are not currently available, or zero if the end of the stream is reached.
+ /// </returns>
+ private unsafe int ReadNative(byte[] array, int offset, int count)
+ {
+ FlushWriteBuffer(); // we're about to read; dump the write buffer
+
+ VerifyOSHandlePosition();
+
+ int bytesRead;
+ fixed (byte* bufPtr = array)
+ {
+ bytesRead = CheckFileCall(Interop.Sys.Read(_fileHandle, bufPtr + offset, count));
+ Debug.Assert(bytesRead <= count);
+ }
+ _filePosition += bytesRead;
+ return bytesRead;
+ }
+
+ /// <summary>
+ /// Asynchronously reads a sequence of bytes from the current stream and advances
+ /// the position within the stream by the number of bytes read.
+ /// </summary>
+ /// <param name="buffer">The buffer to write the data into.</param>
+ /// <param name="offset">The byte offset in buffer at which to begin writing data from the stream.</param>
+ /// <param name="count">The maximum number of bytes to read.</param>
+ /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+ /// <returns>A task that represents the asynchronous read operation.</returns>
+ private Task<int> ReadAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ if (_useAsyncIO)
+ {
+ if (!CanRead) // match Windows behavior; this gets thrown synchronously
+ {
+ throw Error.GetReadNotSupported();
+ }
+
+ // Serialize operations using the semaphore.
+ Task waitTask = _asyncState.WaitAsync();
+
+ // If we got ownership immediately, and if there's enough data in our buffer
+ // to satisfy the full request of the caller, hand back the buffered data.
+ // While it would be a legal implementation of the Read contract, we don't
+ // hand back here less than the amount requested so as to match the behavior
+ // in ReadCore that will make a native call to try to fulfill the remainder
+ // of the request.
+ if (waitTask.Status == TaskStatus.RanToCompletion)
+ {
+ int numBytesAvailable = _readLength - _readPos;
+ if (numBytesAvailable >= count)
+ {
+ try
+ {
+ PrepareForReading();
+
+ Buffer.BlockCopy(GetBuffer(), _readPos, buffer, offset, count);
+ _readPos += count;
+
+ return _asyncState._lastSuccessfulReadTask != null && _asyncState._lastSuccessfulReadTask.Result == count ?
+ _asyncState._lastSuccessfulReadTask :
+ (_asyncState._lastSuccessfulReadTask = Task.FromResult(count));
+ }
+ catch (Exception exc)
+ {
+ return Task.FromException<int>(exc);
+ }
+ finally
+ {
+ _asyncState.Release();
+ }
+ }
+ }
+
+ // Otherwise, issue the whole request asynchronously.
+ _asyncState.Update(buffer, offset, count);
+ return waitTask.ContinueWith((t, s) =>
+ {
+ // The options available on Unix for writing asynchronously to an arbitrary file
+ // handle typically amount to just using another thread to do the synchronous write,
+ // which is exactly what this implementation does. This does mean there are subtle
+ // differences in certain FileStream behaviors between Windows and Unix when multiple
+ // asynchronous operations are issued against the stream to execute concurrently; on
+ // Unix the operations will be serialized due to the usage of a semaphore, but the
+ // position /length information won't be updated until after the write has completed,
+ // whereas on Windows it may happen before the write has completed.
+
+ Debug.Assert(t.Status == TaskStatus.RanToCompletion);
+ var thisRef = (FileStream)s;
+ try
+ {
+ byte[] b = thisRef._asyncState._buffer;
+ thisRef._asyncState._buffer = null; // remove reference to user's buffer
+ return thisRef.ReadCore(b, thisRef._asyncState._offset, thisRef._asyncState._count);
+ }
+ finally { thisRef._asyncState.Release(); }
+ }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+ else
+ {
+ return base.ReadAsync(buffer, offset, count, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Reads a byte from the stream and advances the position within the stream
+ /// by one byte, or returns -1 if at the end of the stream.
+ /// </summary>
+ /// <returns>The unsigned byte cast to an Int32, or -1 if at the end of the stream.</returns>
+ public override int ReadByte()
+ {
+ if (_useAsyncIO)
+ {
+ _asyncState.Wait();
+ try { return ReadByteCore(); }
+ finally { _asyncState.Release(); }
+ }
+ else
+ {
+ return ReadByteCore();
+ }
+ }
+
+ /// <summary>Writes a block of bytes to the file stream.</summary>
+ /// <param name="array">The buffer containing data to write to the stream.</param>
+ /// <param name="offset">The zero-based byte offset in array from which to begin copying bytes to the stream.</param>
+ /// <param name="count">The maximum number of bytes to write.</param>
+ public override void Write(byte[] array, int offset, int count)
+ {
+ ValidateReadWriteArgs(array, offset, count);
+
+ if (_useAsyncIO)
+ {
+ _asyncState.Wait();
+ try { WriteCore(array, offset, count); }
+ finally { _asyncState.Release(); }
+ }
+ else
+ {
+ WriteCore(array, offset, count);
+ }
+ }
+
+ /// <summary>Writes a block of bytes to the file stream.</summary>
+ /// <param name="array">The buffer containing data to write to the stream.</param>
+ /// <param name="offset">The zero-based byte offset in array from which to begin copying bytes to the stream.</param>
+ /// <param name="count">The maximum number of bytes to write.</param>
+ private void WriteCore(byte[] array, int offset, int count)
+ {
+ PrepareForWriting();
+
+ // If no data is being written, nothing more to do.
+ if (count == 0)
+ {
+ return;
+ }
+
+ // If there's already data in our write buffer, then we need to go through
+ // our buffer to ensure data isn't corrupted.
+ if (_writePos > 0)
+ {
+ // If there's space remaining in the buffer, then copy as much as
+ // we can from the user's buffer into ours.
+ int spaceRemaining = _bufferLength - _writePos;
+ if (spaceRemaining > 0)
+ {
+ int bytesToCopy = Math.Min(spaceRemaining, count);
+ Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, bytesToCopy);
+ _writePos += bytesToCopy;
+
+ // If we've successfully copied all of the user's data, we're done.
+ if (count == bytesToCopy)
+ {
+ return;
+ }
+
+ // Otherwise, keep track of how much more data needs to be handled.
+ offset += bytesToCopy;
+ count -= bytesToCopy;
+ }
+
+ // At this point, the buffer is full, so flush it out.
+ FlushWriteBuffer();
+ }
+
+ // Our buffer is now empty. If using the buffer would slow things down (because
+ // the user's looking to write more data than we can store in the buffer),
+ // skip the buffer. Otherwise, put the remaining data into the buffer.
+ Debug.Assert(_writePos == 0);
+ if (count >= _bufferLength)
+ {
+ WriteNative(array, offset, count);
+ }
+ else
+ {
+ Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, count);
+ _writePos = count;
+ }
+ }
+
+ /// <summary>Unbuffered, writes a block of bytes to the file stream.</summary>
+ /// <param name="array">The buffer containing data to write to the stream.</param>
+ /// <param name="offset">The zero-based byte offset in array from which to begin copying bytes to the stream.</param>
+ /// <param name="count">The maximum number of bytes to write.</param>
+ private unsafe void WriteNative(byte[] array, int offset, int count)
+ {
+ VerifyOSHandlePosition();
+
+ fixed (byte* bufPtr = array)
+ {
+ while (count > 0)
+ {
+ int bytesWritten = CheckFileCall(Interop.Sys.Write(_fileHandle, bufPtr + offset, count));
+ Debug.Assert(bytesWritten <= count);
+
+ _filePosition += bytesWritten;
+ count -= bytesWritten;
+ offset += bytesWritten;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Asynchronously writes a sequence of bytes to the current stream, advances
+ /// the current position within this stream by the number of bytes written, and
+ /// monitors cancellation requests.
+ /// </summary>
+ /// <param name="buffer">The buffer to write data from.</param>
+ /// <param name="offset">The zero-based byte offset in buffer from which to begin copying bytes to the stream.</param>
+ /// <param name="count">The maximum number of bytes to write.</param>
+ /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+ /// <returns>A task that represents the asynchronous write operation.</returns>
+ private Task WriteAsyncInternal(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled(cancellationToken);
+
+ if (_fileHandle.IsClosed)
+ throw Error.GetFileNotOpen();
+
+ if (_useAsyncIO)
+ {
+ if (!CanWrite) // match Windows behavior; this gets thrown synchronously
+ {
+ throw Error.GetWriteNotSupported();
+ }
+
+ // Serialize operations using the semaphore.
+ Task waitTask = _asyncState.WaitAsync();
+
+ // If we got ownership immediately, and if there's enough space in our buffer
+ // to buffer the entire write request, then do so and we're done.
+ if (waitTask.Status == TaskStatus.RanToCompletion)
+ {
+ int spaceRemaining = _bufferLength - _writePos;
+ if (spaceRemaining >= count)
+ {
+ try
+ {
+ PrepareForWriting();
+
+ Buffer.BlockCopy(buffer, offset, GetBuffer(), _writePos, count);
+ _writePos += count;
+
+ return Task.CompletedTask;
+ }
+ catch (Exception exc)
+ {
+ return Task.FromException(exc);
+ }
+ finally
+ {
+ _asyncState.Release();
+ }
+ }
+ }
+
+ // Otherwise, issue the whole request asynchronously.
+ _asyncState.Update(buffer, offset, count);
+ return waitTask.ContinueWith((t, s) =>
+ {
+ // The options available on Unix for writing asynchronously to an arbitrary file
+ // handle typically amount to just using another thread to do the synchronous write,
+ // which is exactly what this implementation does. This does mean there are subtle
+ // differences in certain FileStream behaviors between Windows and Unix when multiple
+ // asynchronous operations are issued against the stream to execute concurrently; on
+ // Unix the operations will be serialized due to the usage of a semaphore, but the
+ // position /length information won't be updated until after the write has completed,
+ // whereas on Windows it may happen before the write has completed.
+
+ Debug.Assert(t.Status == TaskStatus.RanToCompletion);
+ var thisRef = (FileStream)s;
+ try
+ {
+ byte[] b = thisRef._asyncState._buffer;
+ thisRef._asyncState._buffer = null; // remove reference to user's buffer
+ thisRef.WriteCore(b, thisRef._asyncState._offset, thisRef._asyncState._count);
+ }
+ finally { thisRef._asyncState.Release(); }
+ }, this, CancellationToken.None, TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
+ }
+ else
+ {
+ return base.WriteAsync(buffer, offset, count, cancellationToken);
+ }
+ }
+
+ /// <summary>
+ /// Writes a byte to the current position in the stream and advances the position
+ /// within the stream by one byte.
+ /// </summary>
+ /// <param name="value">The byte to write to the stream.</param>
+ public override void WriteByte(byte value) // avoids an array allocation in the base implementation
+ {
+ if (_useAsyncIO)
+ {
+ _asyncState.Wait();
+ try { WriteByteCore(value); }
+ finally { _asyncState.Release(); }
+ }
+ else
+ {
+ WriteByteCore(value);
+ }
+ }
+
+ /// <summary>Sets the current position of this stream to the given value.</summary>
+ /// <param name="offset">The point relative to origin from which to begin seeking. </param>
+ /// <param name="origin">
+ /// Specifies the beginning, the end, or the current position as a reference
+ /// point for offset, using a value of type SeekOrigin.
+ /// </param>
+ /// <returns>The new position in the stream.</returns>
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ if (origin < SeekOrigin.Begin || origin > SeekOrigin.End)
+ {
+ throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin));
+ }
+ if (_fileHandle.IsClosed)
+ {
+ throw Error.GetFileNotOpen();
+ }
+ if (!CanSeek)
+ {
+ throw Error.GetSeekNotSupported();
+ }
+
+ VerifyOSHandlePosition();
+
+ // Flush our write/read buffer. FlushWrite will output any write buffer we have and reset _bufferWritePos.
+ // We don't call FlushRead, as that will do an unnecessary seek to rewind the read buffer, and since we're
+ // about to seek and update our position, we can simply update the offset as necessary and reset our read
+ // position and length to 0. (In the future, for some simple cases we could potentially add an optimization
+ // here to just move data around in the buffer for short jumps, to avoid re-reading the data from disk.)
+ FlushWriteBuffer();
+ if (origin == SeekOrigin.Current)
+ {
+ offset -= (_readLength - _readPos);
+ }
+ _readPos = _readLength = 0;
+
+ // Keep track of where we were, in case we're in append mode and need to verify
+ long oldPos = 0;
+ if (_appendStart >= 0)
+ {
+ oldPos = SeekCore(0, SeekOrigin.Current);
+ }
+
+ // Jump to the new location
+ long pos = SeekCore(offset, origin);
+
+ // Prevent users from overwriting data in a file that was opened in append mode.
+ if (_appendStart != -1 && pos < _appendStart)
+ {
+ SeekCore(oldPos, SeekOrigin.Begin);
+ throw new IOException(SR.IO_SeekAppendOverwrite);
+ }
+
+ // Return the new position
+ return pos;
+ }
+
+ /// <summary>Sets the current position of this stream to the given value.</summary>
+ /// <param name="offset">The point relative to origin from which to begin seeking. </param>
+ /// <param name="origin">
+ /// Specifies the beginning, the end, or the current position as a reference
+ /// point for offset, using a value of type SeekOrigin.
+ /// </param>
+ /// <returns>The new position in the stream.</returns>
+ private long SeekCore(long offset, SeekOrigin origin)
+ {
+ Debug.Assert(!_fileHandle.IsClosed && (GetType() != typeof(FileStream) || CanSeek)); // verify that we can seek, but only if CanSeek won't be a virtual call (which could happen in the ctor)
+ Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End);
+
+ long pos = CheckFileCall(Interop.Sys.LSeek(_fileHandle, offset, (Interop.Sys.SeekWhence)(int)origin)); // SeekOrigin values are the same as Interop.libc.SeekWhence values
+ _filePosition = pos;
+ return pos;
+ }
+
+ private long CheckFileCall(long result, bool ignoreNotSupported = false)
+ {
+ if (result < 0)
+ {
+ Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo();
+ if (!(ignoreNotSupported && errorInfo.Error == Interop.Error.ENOTSUP))
+ {
+ throw Interop.GetExceptionForIoErrno(errorInfo, _path, isDirectory: false);
+ }
+ }
+
+ return result;
+ }
+
+ private int CheckFileCall(int result, bool ignoreNotSupported = false)
+ {
+ CheckFileCall((long)result, ignoreNotSupported);
+
+ return result;
+ }
+
+ /// <summary>State used when the stream is in async mode.</summary>
+ private sealed class AsyncState : SemaphoreSlim
+ {
+ /// <summary>The caller's buffer currently being used by the active async operation.</summary>
+ internal byte[] _buffer;
+ /// <summary>The caller's offset currently being used by the active async operation.</summary>
+ internal int _offset;
+ /// <summary>The caller's count currently being used by the active async operation.</summary>
+ internal int _count;
+ /// <summary>The last task successfully, synchronously returned task from ReadAsync.</summary>
+ internal Task<int> _lastSuccessfulReadTask;
+
+ /// <summary>Initialize the AsyncState.</summary>
+ internal AsyncState() : base(initialCount: 1, maxCount: 1) { }
+
+ /// <summary>Sets the active buffer, offset, and count.</summary>
+ internal void Update(byte[] buffer, int offset, int count)
+ {
+ _buffer = buffer;
+ _offset = offset;
+ _count = count;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileStream.Win32.cs b/src/mscorlib/shared/System/IO/FileStream.Win32.cs
new file mode 100644
index 0000000000..0045ebeaf8
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileStream.Win32.cs
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Buffers;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Win32.SafeHandles;
+using System.Runtime.CompilerServices;
+
+namespace System.IO
+{
+ public partial class FileStream : Stream
+ {
+ private SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options)
+ {
+ Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
+
+ int fAccess =
+ ((_access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) |
+ ((_access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0);
+
+ // Our Inheritable bit was stolen from Windows, but should be set in
+ // the security attributes class. Don't leave this bit set.
+ share &= ~FileShare.Inheritable;
+
+ // Must use a valid Win32 constant here...
+ if (mode == FileMode.Append)
+ mode = FileMode.OpenOrCreate;
+
+ int flagsAndAttributes = (int)options;
+
+ // For mitigating local elevation of privilege attack through named pipes
+ // make sure we always call CreateFile with SECURITY_ANONYMOUS so that the
+ // named pipe server can't impersonate a high privileged client security context
+ flagsAndAttributes |= (Interop.Kernel32.SecurityOptions.SECURITY_SQOS_PRESENT | Interop.Kernel32.SecurityOptions.SECURITY_ANONYMOUS);
+
+ // Don't pop up a dialog for reading from an empty floppy drive
+ uint oldMode = Interop.Kernel32.SetErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS);
+ try
+ {
+ SafeFileHandle fileHandle = Interop.Kernel32.CreateFile(_path, fAccess, share, ref secAttrs, mode, flagsAndAttributes, IntPtr.Zero);
+ fileHandle.IsAsync = _useAsyncIO;
+
+ if (fileHandle.IsInvalid)
+ {
+ // Return a meaningful exception with the full path.
+
+ // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
+ // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
+ // probably be consistent w/ every other directory.
+ int errorCode = Marshal.GetLastWin32Error();
+
+ if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path))
+ errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
+
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
+ }
+
+ int fileType = Interop.Kernel32.GetFileType(fileHandle);
+ if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK)
+ {
+ fileHandle.Dispose();
+ throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles);
+ }
+
+ return fileHandle;
+ }
+ finally
+ {
+ Interop.Kernel32.SetErrorMode(oldMode);
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileStream.WinRT.cs b/src/mscorlib/shared/System/IO/FileStream.WinRT.cs
new file mode 100644
index 0000000000..062b160b57
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileStream.WinRT.cs
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32.SafeHandles;
+using System.Runtime.InteropServices;
+
+namespace System.IO
+{
+ public partial class FileStream : Stream
+ {
+ private unsafe SafeFileHandle OpenHandle(FileMode mode, FileShare share, FileOptions options)
+ {
+ Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = GetSecAttrs(share);
+
+ int fAccess =
+ ((_access & FileAccess.Read) == FileAccess.Read ? GENERIC_READ : 0) |
+ ((_access & FileAccess.Write) == FileAccess.Write ? GENERIC_WRITE : 0);
+
+ // Our Inheritable bit was stolen from Windows, but should be set in
+ // the security attributes class. Don't leave this bit set.
+ share &= ~FileShare.Inheritable;
+
+ // Must use a valid Win32 constant here...
+ if (mode == FileMode.Append)
+ mode = FileMode.OpenOrCreate;
+
+ Interop.Kernel32.CREATEFILE2_EXTENDED_PARAMETERS parameters = new Interop.Kernel32.CREATEFILE2_EXTENDED_PARAMETERS();
+ parameters.dwSize = (uint)sizeof(Interop.Kernel32.CREATEFILE2_EXTENDED_PARAMETERS);
+ parameters.dwFileFlags = (uint)options;
+ parameters.lpSecurityAttributes = &secAttrs;
+
+ SafeFileHandle fileHandle = Interop.Kernel32.CreateFile2(
+ lpFileName: _path,
+ dwDesiredAccess: fAccess,
+ dwShareMode: share,
+ dwCreationDisposition: mode,
+ pCreateExParams: &parameters);
+
+ fileHandle.IsAsync = _useAsyncIO;
+
+ if (fileHandle.IsInvalid)
+ {
+ // Return a meaningful exception with the full path.
+
+ // NT5 oddity - when trying to open "C:\" as a Win32FileStream,
+ // we usually get ERROR_PATH_NOT_FOUND from the OS. We should
+ // probably be consistent w/ every other directory.
+ int errorCode = Marshal.GetLastWin32Error();
+
+ if (errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND && _path.Length == PathInternal.GetRootLength(_path))
+ errorCode = Interop.Errors.ERROR_ACCESS_DENIED;
+
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, $"{_path} {options} {fAccess} {share} {mode}");
+ }
+
+ return fileHandle;
+ }
+
+#if PROJECTN
+ // TODO: These internal methods should be removed once we start consuming updated CoreFX builds
+ public static FileStream InternalOpen(string path, int bufferSize = 4096, bool useAsync = true)
+ {
+ return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, useAsync);
+ }
+
+ public static FileStream InternalCreate(string path, int bufferSize = 4096, bool useAsync = true)
+ {
+ return new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize, useAsync);
+ }
+
+ public static FileStream InternalAppend(string path, int bufferSize = 4096, bool useAsync = true)
+ {
+ return new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, bufferSize, useAsync);
+ }
+#endif
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileStream.Windows.cs b/src/mscorlib/shared/System/IO/FileStream.Windows.cs
new file mode 100644
index 0000000000..7c09ae1a1c
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileStream.Windows.cs
@@ -0,0 +1,1717 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Buffers;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Win32.SafeHandles;
+using System.Runtime.CompilerServices;
+
+/*
+ * Win32FileStream supports different modes of accessing the disk - async mode
+ * and sync mode. They are two completely different codepaths in the
+ * sync & async methods (i.e. Read/Write vs. ReadAsync/WriteAsync). File
+ * handles in NT can be opened in only sync or overlapped (async) mode,
+ * and we have to deal with this pain. Stream has implementations of
+ * the sync methods in terms of the async ones, so we'll
+ * call through to our base class to get those methods when necessary.
+ *
+ * Also buffering is added into Win32FileStream as well. Folded in the
+ * code from BufferedStream, so all the comments about it being mostly
+ * aggressive (and the possible perf improvement) apply to Win32FileStream as
+ * well. Also added some buffering to the async code paths.
+ *
+ * Class Invariants:
+ * The class has one buffer, shared for reading & writing. It can only be
+ * used for one or the other at any point in time - not both. The following
+ * should be true:
+ * 0 <= _readPos <= _readLen < _bufferSize
+ * 0 <= _writePos < _bufferSize
+ * _readPos == _readLen && _readPos > 0 implies the read buffer is valid,
+ * but we're at the end of the buffer.
+ * _readPos == _readLen == 0 means the read buffer contains garbage.
+ * Either _writePos can be greater than 0, or _readLen & _readPos can be
+ * greater than zero, but neither can be greater than zero at the same time.
+ *
+ */
+
+namespace System.IO
+{
+ public partial class FileStream : Stream
+ {
+ private bool _canSeek;
+ private bool _isPipe; // Whether to disable async buffering code.
+ private long _appendStart; // When appending, prevent overwriting file.
+
+ private static unsafe IOCompletionCallback s_ioCallback = FileStreamCompletionSource.IOCallback;
+
+ private Task<int> _lastSynchronouslyCompletedTask = null; // cached task for read ops that complete synchronously
+ private Task _activeBufferOperation = null; // tracks in-progress async ops using the buffer
+ private PreAllocatedOverlapped _preallocatedOverlapped; // optimization for async ops to avoid per-op allocations
+ private FileStreamCompletionSource _currentOverlappedOwner; // async op currently using the preallocated overlapped
+
+ private void Init(FileMode mode, FileShare share)
+ {
+ // Disallow access to all non-file devices from the Win32FileStream
+ // constructors that take a String. Everyone else can call
+ // CreateFile themselves then use the constructor that takes an
+ // IntPtr. Disallows "con:", "com1:", "lpt1:", etc.
+ int fileType = Interop.Kernel32.GetFileType(_fileHandle);
+ if (fileType != Interop.Kernel32.FileTypes.FILE_TYPE_DISK)
+ {
+ _fileHandle.Dispose();
+ throw new NotSupportedException(SR.NotSupported_FileStreamOnNonFiles);
+ }
+
+ // This is necessary for async IO using IO Completion ports via our
+ // managed Threadpool API's. This (theoretically) calls the OS's
+ // BindIoCompletionCallback method, and passes in a stub for the
+ // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped
+ // struct for this request and gets a delegate to a managed callback
+ // from there, which it then calls on a threadpool thread. (We allocate
+ // our native OVERLAPPED structs 2 pointers too large and store EE state
+ // & GC handles there, one to an IAsyncResult, the other to a delegate.)
+ if (_useAsyncIO)
+ {
+ try
+ {
+ _fileHandle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(_fileHandle);
+ }
+ catch (ArgumentException ex)
+ {
+ throw new IOException(SR.IO_BindHandleFailed, ex);
+ }
+ finally
+ {
+ if (_fileHandle.ThreadPoolBinding == null)
+ {
+ // We should close the handle so that the handle is not open until SafeFileHandle GC
+ Debug.Assert(!_exposedHandle, "Are we closing handle that we exposed/not own, how?");
+ _fileHandle.Dispose();
+ }
+ }
+ }
+
+ _canSeek = true;
+
+ // For Append mode...
+ if (mode == FileMode.Append)
+ {
+ _appendStart = SeekCore(0, SeekOrigin.End);
+ }
+ else
+ {
+ _appendStart = -1;
+ }
+ }
+
+ private void InitFromHandle(SafeFileHandle handle)
+ {
+ int handleType = Interop.Kernel32.GetFileType(_fileHandle);
+ Debug.Assert(handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE || handleType == Interop.Kernel32.FileTypes.FILE_TYPE_CHAR, "FileStream was passed an unknown file type!");
+
+ _canSeek = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_DISK;
+ _isPipe = handleType == Interop.Kernel32.FileTypes.FILE_TYPE_PIPE;
+
+ // This is necessary for async IO using IO Completion ports via our
+ // managed Threadpool API's. This calls the OS's
+ // BindIoCompletionCallback method, and passes in a stub for the
+ // LPOVERLAPPED_COMPLETION_ROUTINE. This stub looks at the Overlapped
+ // struct for this request and gets a delegate to a managed callback
+ // from there, which it then calls on a threadpool thread. (We allocate
+ // our native OVERLAPPED structs 2 pointers too large and store EE
+ // state & a handle to a delegate there.)
+ //
+ // If, however, we've already bound this file handle to our completion port,
+ // don't try to bind it again because it will fail. A handle can only be
+ // bound to a single completion port at a time.
+ if (_useAsyncIO && !GetSuppressBindHandle(handle))
+ {
+ try
+ {
+ _fileHandle.ThreadPoolBinding = ThreadPoolBoundHandle.BindHandle(_fileHandle);
+ }
+ catch (Exception ex)
+ {
+ // If you passed in a synchronous handle and told us to use
+ // it asynchronously, throw here.
+ throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle), ex);
+ }
+ }
+ else if (!_useAsyncIO)
+ {
+ if (handleType != Interop.Kernel32.FileTypes.FILE_TYPE_PIPE)
+ VerifyHandleIsSync();
+ }
+
+ if (_canSeek)
+ SeekCore(0, SeekOrigin.Current);
+ else
+ _filePosition = 0;
+ }
+
+ private static bool GetSuppressBindHandle(SafeFileHandle handle)
+ {
+ return handle.IsAsync.HasValue ? handle.IsAsync.Value : false;
+ }
+
+ private unsafe static Interop.Kernel32.SECURITY_ATTRIBUTES GetSecAttrs(FileShare share)
+ {
+ Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = default(Interop.Kernel32.SECURITY_ATTRIBUTES);
+ if ((share & FileShare.Inheritable) != 0)
+ {
+ secAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES();
+ secAttrs.nLength = (uint)sizeof(Interop.Kernel32.SECURITY_ATTRIBUTES);
+
+ secAttrs.bInheritHandle = Interop.BOOL.TRUE;
+ }
+ return secAttrs;
+ }
+
+ // Verifies that this handle supports synchronous IO operations (unless you
+ // didn't open it for either reading or writing).
+ private unsafe void VerifyHandleIsSync()
+ {
+ Debug.Assert(!_useAsyncIO);
+
+ // Do NOT use this method on pipes. Reading or writing to a pipe may
+ // cause an app to block incorrectly, introducing a deadlock (depending
+ // on whether a write will wake up an already-blocked thread or this
+ // Win32FileStream's thread).
+ Debug.Assert(Interop.Kernel32.GetFileType(_fileHandle) != Interop.Kernel32.FileTypes.FILE_TYPE_PIPE);
+
+ byte* bytes = stackalloc byte[1];
+ int numBytesReadWritten;
+ int r = -1;
+
+ // If the handle is a pipe, ReadFile will block until there
+ // has been a write on the other end. We'll just have to deal with it,
+ // For the read end of a pipe, you can mess up and
+ // accidentally read synchronously from an async pipe.
+ if ((_access & FileAccess.Read) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor
+ {
+ r = Interop.Kernel32.ReadFile(_fileHandle, bytes, 0, out numBytesReadWritten, IntPtr.Zero);
+ }
+ else if ((_access & FileAccess.Write) != 0) // don't use the virtual CanRead or CanWrite, as this may be used in the ctor
+ {
+ r = Interop.Kernel32.WriteFile(_fileHandle, bytes, 0, out numBytesReadWritten, IntPtr.Zero);
+ }
+
+ if (r == 0)
+ {
+ int errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid(throwIfInvalidHandle: true);
+ if (errorCode == ERROR_INVALID_PARAMETER)
+ throw new ArgumentException(SR.Arg_HandleNotSync, "handle");
+ }
+ }
+
+ private bool HasActiveBufferOperation
+ {
+ get { return _activeBufferOperation != null && !_activeBufferOperation.IsCompleted; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return _canSeek; }
+ }
+
+ private unsafe long GetLengthInternal()
+ {
+ Interop.Kernel32.FILE_STANDARD_INFO info = new Interop.Kernel32.FILE_STANDARD_INFO();
+
+ if (!Interop.Kernel32.GetFileInformationByHandleEx(_fileHandle, Interop.Kernel32.FILE_INFO_BY_HANDLE_CLASS.FileStandardInfo, out info, (uint)sizeof(Interop.Kernel32.FILE_STANDARD_INFO)))
+ throw Win32Marshal.GetExceptionForLastWin32Error();
+ long len = info.EndOfFile;
+ // If we're writing near the end of the file, we must include our
+ // internal buffer in our Length calculation. Don't flush because
+ // we use the length of the file in our async write method.
+ if (_writePos > 0 && _filePosition + _writePos > len)
+ len = _writePos + _filePosition;
+ return len;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ // Nothing will be done differently based on whether we are
+ // disposing vs. finalizing. This is taking advantage of the
+ // weak ordering between normal finalizable objects & critical
+ // finalizable objects, which I included in the SafeHandle
+ // design for Win32FileStream, which would often "just work" when
+ // finalized.
+ try
+ {
+ if (_fileHandle != null && !_fileHandle.IsClosed)
+ {
+ // Flush data to disk iff we were writing. After
+ // thinking about this, we also don't need to flush
+ // our read position, regardless of whether the handle
+ // was exposed to the user. They probably would NOT
+ // want us to do this.
+ if (_writePos > 0)
+ {
+ FlushWriteBuffer(!disposing);
+ }
+ }
+ }
+ finally
+ {
+ if (_fileHandle != null && !_fileHandle.IsClosed)
+ {
+ if (_fileHandle.ThreadPoolBinding != null)
+ _fileHandle.ThreadPoolBinding.Dispose();
+
+ _fileHandle.Dispose();
+ }
+
+ if (_preallocatedOverlapped != null)
+ _preallocatedOverlapped.Dispose();
+
+ _canSeek = false;
+
+ // Don't set the buffer to null, to avoid a NullReferenceException
+ // when users have a race condition in their code (i.e. they call
+ // Close when calling another method on Stream like Read).
+ //_buffer = null;
+ base.Dispose(disposing);
+ }
+ }
+
+ private void FlushOSBuffer()
+ {
+ if (!Interop.Kernel32.FlushFileBuffers(_fileHandle))
+ {
+ throw Win32Marshal.GetExceptionForLastWin32Error();
+ }
+ }
+
+ // Returns a task that flushes the internal write buffer
+ private Task FlushWriteAsync(CancellationToken cancellationToken)
+ {
+ Debug.Assert(_useAsyncIO);
+ Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWriteAsync!");
+
+ // If the buffer is already flushed, don't spin up the OS write
+ if (_writePos == 0) return Task.CompletedTask;
+
+ Task flushTask = WriteInternalCoreAsync(GetBuffer(), 0, _writePos, cancellationToken);
+ _writePos = 0;
+
+ // Update the active buffer operation
+ _activeBufferOperation = HasActiveBufferOperation ?
+ Task.WhenAll(_activeBufferOperation, flushTask) :
+ flushTask;
+
+ return flushTask;
+ }
+
+ // Writes are buffered. Anytime the buffer fills up
+ // (_writePos + delta > _bufferSize) or the buffer switches to reading
+ // and there is left over data (_writePos > 0), this function must be called.
+ private void FlushWriteBuffer(bool calledFromFinalizer = false)
+ {
+ if (_writePos == 0) return;
+ Debug.Assert(_readPos == 0 && _readLength == 0, "FileStream: Read buffer must be empty in FlushWrite!");
+
+ if (_useAsyncIO)
+ {
+ Task writeTask = FlushWriteAsync(CancellationToken.None);
+ // With our Whidbey async IO & overlapped support for AD unloads,
+ // we don't strictly need to block here to release resources
+ // since that support takes care of the pinning & freeing the
+ // overlapped struct. We need to do this when called from
+ // Close so that the handle is closed when Close returns, but
+ // we don't need to call EndWrite from the finalizer.
+ // Additionally, if we do call EndWrite, we block forever
+ // because AD unloads prevent us from running the managed
+ // callback from the IO completion port. Blocking here when
+ // called from the finalizer during AD unload is clearly wrong,
+ // but we can't use any sort of test for whether the AD is
+ // unloading because if we weren't unloading, an AD unload
+ // could happen on a separate thread before we call EndWrite.
+ if (!calledFromFinalizer)
+ {
+ writeTask.GetAwaiter().GetResult();
+ }
+ }
+ else
+ {
+ WriteCore(GetBuffer(), 0, _writePos);
+ }
+
+ _writePos = 0;
+ }
+
+ private void SetLengthInternal(long value)
+ {
+ // Handle buffering updates.
+ if (_writePos > 0)
+ {
+ FlushWriteBuffer();
+ }
+ else if (_readPos < _readLength)
+ {
+ FlushReadBuffer();
+ }
+ _readPos = 0;
+ _readLength = 0;
+
+ if (_appendStart != -1 && value < _appendStart)
+ throw new IOException(SR.IO_SetLengthAppendTruncate);
+ SetLengthCore(value);
+ }
+
+ // We absolutely need this method broken out so that WriteInternalCoreAsync can call
+ // a method without having to go through buffering code that might call FlushWrite.
+ private void SetLengthCore(long value)
+ {
+ Debug.Assert(value >= 0, "value >= 0");
+ long origPos = _filePosition;
+
+ VerifyOSHandlePosition();
+ if (_filePosition != value)
+ SeekCore(value, SeekOrigin.Begin);
+ if (!Interop.Kernel32.SetEndOfFile(_fileHandle))
+ {
+ int errorCode = Marshal.GetLastWin32Error();
+ if (errorCode == Interop.Errors.ERROR_INVALID_PARAMETER)
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_FileLengthTooBig);
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode);
+ }
+ // Return file pointer to where it was before setting length
+ if (origPos != value)
+ {
+ if (origPos < value)
+ SeekCore(origPos, SeekOrigin.Begin);
+ else
+ SeekCore(0, SeekOrigin.End);
+ }
+ }
+
+ // Instance method to help code external to this MarshalByRefObject avoid
+ // accessing its fields by ref. This avoids a compiler warning.
+ private FileStreamCompletionSource CompareExchangeCurrentOverlappedOwner(FileStreamCompletionSource newSource, FileStreamCompletionSource existingSource) => Interlocked.CompareExchange(ref _currentOverlappedOwner, newSource, existingSource);
+
+ public override int Read(byte[] array, int offset, int count)
+ {
+ ValidateReadWriteArgs(array, offset, count);
+ return ReadCore(array, offset, count);
+ }
+
+ private int ReadCore(byte[] array, int offset, int count)
+ {
+ Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength),
+ "We're either reading or writing, but not both.");
+
+ bool isBlocked = false;
+ int n = _readLength - _readPos;
+ // if the read buffer is empty, read into either user's array or our
+ // buffer, depending on number of bytes user asked for and buffer size.
+ if (n == 0)
+ {
+ if (!CanRead) throw Error.GetReadNotSupported();
+ if (_writePos > 0) FlushWriteBuffer();
+ if (!CanSeek || (count >= _bufferLength))
+ {
+ n = ReadNative(array, offset, count);
+ // Throw away read buffer.
+ _readPos = 0;
+ _readLength = 0;
+ return n;
+ }
+ n = ReadNative(GetBuffer(), 0, _bufferLength);
+ if (n == 0) return 0;
+ isBlocked = n < _bufferLength;
+ _readPos = 0;
+ _readLength = n;
+ }
+ // Now copy min of count or numBytesAvailable (i.e. near EOF) to array.
+ if (n > count) n = count;
+ Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n);
+ _readPos += n;
+
+ // We may have read less than the number of bytes the user asked
+ // for, but that is part of the Stream contract. Reading again for
+ // more data may cause us to block if we're using a device with
+ // no clear end of file, such as a serial port or pipe. If we
+ // blocked here & this code was used with redirected pipes for a
+ // process's standard output, this can lead to deadlocks involving
+ // two processes. But leave this here for files to avoid what would
+ // probably be a breaking change. --
+
+ // If we are reading from a device with no clear EOF like a
+ // serial port or a pipe, this will cause us to block incorrectly.
+ if (!_isPipe)
+ {
+ // If we hit the end of the buffer and didn't have enough bytes, we must
+ // read some more from the underlying stream. However, if we got
+ // fewer bytes from the underlying stream than we asked for (i.e. we're
+ // probably blocked), don't ask for more bytes.
+ if (n < count && !isBlocked)
+ {
+ Debug.Assert(_readPos == _readLength, "Read buffer should be empty!");
+ int moreBytesRead = ReadNative(array, offset + n, count - n);
+ n += moreBytesRead;
+ // We've just made our buffer inconsistent with our position
+ // pointer. We must throw away the read buffer.
+ _readPos = 0;
+ _readLength = 0;
+ }
+ }
+
+ return n;
+ }
+
+ [Conditional("DEBUG")]
+ private void AssertCanRead(byte[] buffer, int offset, int count)
+ {
+ Debug.Assert(!_fileHandle.IsClosed, "!_fileHandle.IsClosed");
+ Debug.Assert(CanRead, "CanRead");
+ Debug.Assert(buffer != null, "buffer != null");
+ Debug.Assert(_writePos == 0, "_writePos == 0");
+ Debug.Assert(offset >= 0, "offset is negative");
+ Debug.Assert(count >= 0, "count is negative");
+ }
+
+ private unsafe int ReadNative(byte[] buffer, int offset, int count)
+ {
+ AssertCanRead(buffer, offset, count);
+
+ if (_useAsyncIO)
+ return ReadNativeAsync(buffer, offset, count, 0, CancellationToken.None).GetAwaiter().GetResult();
+
+ // Make sure we are reading from the right spot
+ VerifyOSHandlePosition();
+
+ int errorCode = 0;
+ int r = ReadFileNative(_fileHandle, buffer, offset, count, null, out errorCode);
+
+ if (r == -1)
+ {
+ // For pipes, ERROR_BROKEN_PIPE is the normal end of the pipe.
+ if (errorCode == ERROR_BROKEN_PIPE)
+ {
+ r = 0;
+ }
+ else
+ {
+ if (errorCode == ERROR_INVALID_PARAMETER)
+ throw new ArgumentException(SR.Arg_HandleNotSync, "_fileHandle");
+
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode);
+ }
+ }
+ Debug.Assert(r >= 0, "FileStream's ReadNative is likely broken.");
+ _filePosition += r;
+
+ return r;
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ if (origin < SeekOrigin.Begin || origin > SeekOrigin.End)
+ throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin));
+ if (_fileHandle.IsClosed) throw Error.GetFileNotOpen();
+ if (!CanSeek) throw Error.GetSeekNotSupported();
+
+ Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
+
+ // If we've got bytes in our buffer to write, write them out.
+ // If we've read in and consumed some bytes, we'll have to adjust
+ // our seek positions ONLY IF we're seeking relative to the current
+ // position in the stream. This simulates doing a seek to the new
+ // position, then a read for the number of bytes we have in our buffer.
+ if (_writePos > 0)
+ {
+ FlushWriteBuffer();
+ }
+ else if (origin == SeekOrigin.Current)
+ {
+ // Don't call FlushRead here, which would have caused an infinite
+ // loop. Simply adjust the seek origin. This isn't necessary
+ // if we're seeking relative to the beginning or end of the stream.
+ offset -= (_readLength - _readPos);
+ }
+ _readPos = _readLength = 0;
+
+ // Verify that internal position is in sync with the handle
+ VerifyOSHandlePosition();
+
+ long oldPos = _filePosition + (_readPos - _readLength);
+ long pos = SeekCore(offset, origin);
+
+ // Prevent users from overwriting data in a file that was opened in
+ // append mode.
+ if (_appendStart != -1 && pos < _appendStart)
+ {
+ SeekCore(oldPos, SeekOrigin.Begin);
+ throw new IOException(SR.IO_SeekAppendOverwrite);
+ }
+
+ // We now must update the read buffer. We can in some cases simply
+ // update _readPos within the buffer, copy around the buffer so our
+ // Position property is still correct, and avoid having to do more
+ // reads from the disk. Otherwise, discard the buffer's contents.
+ if (_readLength > 0)
+ {
+ // We can optimize the following condition:
+ // oldPos - _readPos <= pos < oldPos + _readLen - _readPos
+ if (oldPos == pos)
+ {
+ if (_readPos > 0)
+ {
+ //Console.WriteLine("Seek: seeked for 0, adjusting buffer back by: "+_readPos+" _readLen: "+_readLen);
+ Buffer.BlockCopy(GetBuffer(), _readPos, GetBuffer(), 0, _readLength - _readPos);
+ _readLength -= _readPos;
+ _readPos = 0;
+ }
+ // If we still have buffered data, we must update the stream's
+ // position so our Position property is correct.
+ if (_readLength > 0)
+ SeekCore(_readLength, SeekOrigin.Current);
+ }
+ else if (oldPos - _readPos < pos && pos < oldPos + _readLength - _readPos)
+ {
+ int diff = (int)(pos - oldPos);
+ //Console.WriteLine("Seek: diff was "+diff+", readpos was "+_readPos+" adjusting buffer - shrinking by "+ (_readPos + diff));
+ Buffer.BlockCopy(GetBuffer(), _readPos + diff, GetBuffer(), 0, _readLength - (_readPos + diff));
+ _readLength -= (_readPos + diff);
+ _readPos = 0;
+ if (_readLength > 0)
+ SeekCore(_readLength, SeekOrigin.Current);
+ }
+ else
+ {
+ // Lose the read buffer.
+ _readPos = 0;
+ _readLength = 0;
+ }
+ Debug.Assert(_readLength >= 0 && _readPos <= _readLength, "_readLen should be nonnegative, and _readPos should be less than or equal _readLen");
+ Debug.Assert(pos == Position, "Seek optimization: pos != Position! Buffer math was mangled.");
+ }
+ return pos;
+ }
+
+ // This doesn't do argument checking. Necessary for SetLength, which must
+ // set the file pointer beyond the end of the file. This will update the
+ // internal position
+ // This is called during construction so it should avoid any virtual
+ // calls
+ private long SeekCore(long offset, SeekOrigin origin)
+ {
+ Debug.Assert(!_fileHandle.IsClosed && _canSeek, "!_handle.IsClosed && _parent.CanSeek");
+ Debug.Assert(origin >= SeekOrigin.Begin && origin <= SeekOrigin.End, "origin>=SeekOrigin.Begin && origin<=SeekOrigin.End");
+ long ret = 0;
+
+ if (!Interop.Kernel32.SetFilePointerEx(_fileHandle, offset, out ret, (uint)origin))
+ {
+ int errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid();
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode);
+ }
+
+ _filePosition = ret;
+ return ret;
+ }
+
+ partial void OnBufferAllocated()
+ {
+ Debug.Assert(_buffer != null);
+ Debug.Assert(_preallocatedOverlapped == null);
+
+ if (_useAsyncIO)
+ _preallocatedOverlapped = new PreAllocatedOverlapped(s_ioCallback, this, _buffer);
+ }
+
+ public override void Write(byte[] array, int offset, int count)
+ {
+ ValidateReadWriteArgs(array, offset, count);
+
+ if (_writePos == 0)
+ {
+ // Ensure we can write to the stream, and ready buffer for writing.
+ if (!CanWrite) throw Error.GetWriteNotSupported();
+ if (_readPos < _readLength) FlushReadBuffer();
+ _readPos = 0;
+ _readLength = 0;
+ }
+
+ // If our buffer has data in it, copy data from the user's array into
+ // the buffer, and if we can fit it all there, return. Otherwise, write
+ // the buffer to disk and copy any remaining data into our buffer.
+ // The assumption here is memcpy is cheaper than disk (or net) IO.
+ // (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy)
+ // So the extra copying will reduce the total number of writes, in
+ // non-pathological cases (i.e. write 1 byte, then write for the buffer
+ // size repeatedly)
+ if (_writePos > 0)
+ {
+ int numBytes = _bufferLength - _writePos; // space left in buffer
+ if (numBytes > 0)
+ {
+ if (numBytes > count)
+ numBytes = count;
+ Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, numBytes);
+ _writePos += numBytes;
+ if (count == numBytes) return;
+ offset += numBytes;
+ count -= numBytes;
+ }
+ // Reset our buffer. We essentially want to call FlushWrite
+ // without calling Flush on the underlying Stream.
+
+ if (_useAsyncIO)
+ {
+ WriteInternalCoreAsync(GetBuffer(), 0, _writePos, CancellationToken.None).GetAwaiter().GetResult();
+ }
+ else
+ {
+ WriteCore(GetBuffer(), 0, _writePos);
+ }
+ _writePos = 0;
+ }
+ // If the buffer would slow writes down, avoid buffer completely.
+ if (count >= _bufferLength)
+ {
+ Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted.");
+ WriteCore(array, offset, count);
+ return;
+ }
+ else if (count == 0)
+ {
+ return; // Don't allocate a buffer then call memcpy for 0 bytes.
+ }
+
+ // Copy remaining bytes into buffer, to write at a later date.
+ Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, count);
+ _writePos = count;
+ return;
+ }
+
+ private unsafe void WriteCore(byte[] buffer, int offset, int count)
+ {
+ Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
+ Debug.Assert(CanWrite, "_parent.CanWrite");
+
+ Debug.Assert(buffer != null, "buffer != null");
+ Debug.Assert(_readPos == _readLength, "_readPos == _readLen");
+ Debug.Assert(offset >= 0, "offset is negative");
+ Debug.Assert(count >= 0, "count is negative");
+ if (_useAsyncIO)
+ {
+ WriteInternalCoreAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
+ return;
+ }
+
+ // Make sure we are writing to the position that we think we are
+ VerifyOSHandlePosition();
+
+ int errorCode = 0;
+ int r = WriteFileNative(_fileHandle, buffer, offset, count, null, out errorCode);
+
+ if (r == -1)
+ {
+ // For pipes, ERROR_NO_DATA is not an error, but the pipe is closing.
+ if (errorCode == ERROR_NO_DATA)
+ {
+ r = 0;
+ }
+ else
+ {
+ // ERROR_INVALID_PARAMETER may be returned for writes
+ // where the position is too large (i.e. writing at Int64.MaxValue
+ // on Win9x) OR for synchronous writes to a handle opened
+ // asynchronously.
+ if (errorCode == ERROR_INVALID_PARAMETER)
+ throw new IOException(SR.IO_FileTooLongOrHandleNotSync);
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode);
+ }
+ }
+ Debug.Assert(r >= 0, "FileStream's WriteCore is likely broken.");
+ _filePosition += r;
+ return;
+ }
+
+ private Task<int> ReadAsyncInternal(byte[] array, int offset, int numBytes, CancellationToken cancellationToken)
+ {
+ // If async IO is not supported on this platform or
+ // if this Win32FileStream was not opened with FileOptions.Asynchronous.
+ if (!_useAsyncIO)
+ {
+ return base.ReadAsync(array, offset, numBytes, cancellationToken);
+ }
+
+ if (!CanRead) throw Error.GetReadNotSupported();
+
+ Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
+
+ if (_isPipe)
+ {
+ // Pipes are tricky, at least when you have 2 different pipes
+ // that you want to use simultaneously. When redirecting stdout
+ // & stderr with the Process class, it's easy to deadlock your
+ // parent & child processes when doing writes 4K at a time. The
+ // OS appears to use a 4K buffer internally. If you write to a
+ // pipe that is full, you will block until someone read from
+ // that pipe. If you try reading from an empty pipe and
+ // Win32FileStream's ReadAsync blocks waiting for data to fill it's
+ // internal buffer, you will be blocked. In a case where a child
+ // process writes to stdout & stderr while a parent process tries
+ // reading from both, you can easily get into a deadlock here.
+ // To avoid this deadlock, don't buffer when doing async IO on
+ // pipes. But don't completely ignore buffered data either.
+ if (_readPos < _readLength)
+ {
+ int n = _readLength - _readPos;
+ if (n > numBytes) n = numBytes;
+ Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n);
+ _readPos += n;
+
+ // Return a completed task
+ return TaskFromResultOrCache(n);
+ }
+ else
+ {
+ Debug.Assert(_writePos == 0, "Win32FileStream must not have buffered write data here! Pipes should be unidirectional.");
+ return ReadNativeAsync(array, offset, numBytes, 0, cancellationToken);
+ }
+ }
+
+ Debug.Assert(!_isPipe, "Should not be a pipe.");
+
+ // Handle buffering.
+ if (_writePos > 0) FlushWriteBuffer();
+ if (_readPos == _readLength)
+ {
+ // I can't see how to handle buffering of async requests when
+ // filling the buffer asynchronously, without a lot of complexity.
+ // The problems I see are issuing an async read, we do an async
+ // read to fill the buffer, then someone issues another read
+ // (either synchronously or asynchronously) before the first one
+ // returns. This would involve some sort of complex buffer locking
+ // that we probably don't want to get into, at least not in V1.
+ // If we did a sync read to fill the buffer, we could avoid the
+ // problem, and any async read less than 64K gets turned into a
+ // synchronous read by NT anyways... --
+
+ if (numBytes < _bufferLength)
+ {
+ Task<int> readTask = ReadNativeAsync(GetBuffer(), 0, _bufferLength, 0, cancellationToken);
+ _readLength = readTask.GetAwaiter().GetResult();
+ int n = _readLength;
+ if (n > numBytes) n = numBytes;
+ Buffer.BlockCopy(GetBuffer(), 0, array, offset, n);
+ _readPos = n;
+
+ // Return a completed task (recycling the one above if possible)
+ return (_readLength == n ? readTask : TaskFromResultOrCache(n));
+ }
+ else
+ {
+ // Here we're making our position pointer inconsistent
+ // with our read buffer. Throw away the read buffer's contents.
+ _readPos = 0;
+ _readLength = 0;
+ return ReadNativeAsync(array, offset, numBytes, 0, cancellationToken);
+ }
+ }
+ else
+ {
+ int n = _readLength - _readPos;
+ if (n > numBytes) n = numBytes;
+ Buffer.BlockCopy(GetBuffer(), _readPos, array, offset, n);
+ _readPos += n;
+
+ if (n >= numBytes)
+ {
+ // Return a completed task
+ return TaskFromResultOrCache(n);
+ }
+ else
+ {
+ // For streams with no clear EOF like serial ports or pipes
+ // we cannot read more data without causing an app to block
+ // incorrectly. Pipes don't go down this path
+ // though. This code needs to be fixed.
+ // Throw away read buffer.
+ _readPos = 0;
+ _readLength = 0;
+ return ReadNativeAsync(array, offset + n, numBytes - n, n, cancellationToken);
+ }
+ }
+ }
+
+ unsafe private Task<int> ReadNativeAsync(byte[] bytes, int offset, int numBytes, int numBufferedBytesRead, CancellationToken cancellationToken)
+ {
+ AssertCanRead(bytes, offset, numBytes);
+ Debug.Assert(_useAsyncIO, "ReadNativeAsync doesn't work on synchronous file streams!");
+
+ // Create and store async stream class library specific data in the async result
+
+ FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, numBufferedBytesRead, bytes, cancellationToken);
+ NativeOverlapped* intOverlapped = completionSource.Overlapped;
+
+ // Calculate position in the file we should be at after the read is done
+ if (CanSeek)
+ {
+ long len = Length;
+
+ // Make sure we are reading from the position that we think we are
+ VerifyOSHandlePosition();
+
+ if (_filePosition + numBytes > len)
+ {
+ if (_filePosition <= len)
+ numBytes = (int)(len - _filePosition);
+ else
+ numBytes = 0;
+ }
+
+ // Now set the position to read from in the NativeOverlapped struct
+ // For pipes, we should leave the offset fields set to 0.
+ intOverlapped->OffsetLow = unchecked((int)_filePosition);
+ intOverlapped->OffsetHigh = (int)(_filePosition >> 32);
+
+ // When using overlapped IO, the OS is not supposed to
+ // touch the file pointer location at all. We will adjust it
+ // ourselves. This isn't threadsafe.
+
+ // WriteFile should not update the file pointer when writing
+ // in overlapped mode, according to MSDN. But it does update
+ // the file pointer when writing to a UNC path!
+ // So changed the code below to seek to an absolute
+ // location, not a relative one. ReadFile seems consistent though.
+ SeekCore(numBytes, SeekOrigin.Current);
+ }
+
+ // queue an async ReadFile operation and pass in a packed overlapped
+ int errorCode = 0;
+ int r = ReadFileNative(_fileHandle, bytes, offset, numBytes, intOverlapped, out errorCode);
+ // ReadFile, the OS version, will return 0 on failure. But
+ // my ReadFileNative wrapper returns -1. My wrapper will return
+ // the following:
+ // On error, r==-1.
+ // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
+ // on async requests that completed sequentially, r==0
+ // You will NEVER RELIABLY be able to get the number of bytes
+ // read back from this call when using overlapped structures! You must
+ // not pass in a non-null lpNumBytesRead to ReadFile when using
+ // overlapped structures! This is by design NT behavior.
+ if (r == -1 && numBytes != -1)
+ {
+ // For pipes, when they hit EOF, they will come here.
+ if (errorCode == ERROR_BROKEN_PIPE)
+ {
+ // Not an error, but EOF. AsyncFSCallback will NOT be
+ // called. Call the user callback here.
+
+ // We clear the overlapped status bit for this special case.
+ // Failure to do so looks like we are freeing a pending overlapped later.
+ intOverlapped->InternalLow = IntPtr.Zero;
+ completionSource.SetCompletedSynchronously(0);
+ }
+ else if (errorCode != ERROR_IO_PENDING)
+ {
+ if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere.
+ {
+ SeekCore(0, SeekOrigin.Current);
+ }
+
+ completionSource.ReleaseNativeResource();
+
+ if (errorCode == ERROR_HANDLE_EOF)
+ {
+ throw Error.GetEndOfFile();
+ }
+ else
+ {
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode);
+ }
+ }
+ else
+ {
+ // Only once the IO is pending do we register for cancellation
+ completionSource.RegisterForCancellation();
+ }
+ }
+ else
+ {
+ // Due to a workaround for a race condition in NT's ReadFile &
+ // WriteFile routines, we will always be returning 0 from ReadFileNative
+ // when we do async IO instead of the number of bytes read,
+ // irregardless of whether the operation completed
+ // synchronously or asynchronously. We absolutely must not
+ // set asyncResult._numBytes here, since will never have correct
+ // results.
+ //Console.WriteLine("ReadFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on a separate thread");
+ }
+
+ return completionSource.Task;
+ }
+
+ // Reads a byte from the file stream. Returns the byte cast to an int
+ // or -1 if reading from the end of the stream.
+ public override int ReadByte()
+ {
+ return ReadByteCore();
+ }
+
+ private Task WriteAsyncInternal(byte[] array, int offset, int numBytes, CancellationToken cancellationToken)
+ {
+ // If async IO is not supported on this platform or
+ // if this Win32FileStream was not opened with FileOptions.Asynchronous.
+ if (!_useAsyncIO)
+ {
+ return base.WriteAsync(array, offset, numBytes, cancellationToken);
+ }
+
+ if (!CanWrite) throw Error.GetWriteNotSupported();
+
+ Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
+ Debug.Assert(!_isPipe || (_readPos == 0 && _readLength == 0), "Win32FileStream must not have buffered data here! Pipes should be unidirectional.");
+
+ bool writeDataStoredInBuffer = false;
+ if (!_isPipe) // avoid async buffering with pipes, as doing so can lead to deadlocks (see comments in ReadInternalAsyncCore)
+ {
+ // Ensure the buffer is clear for writing
+ if (_writePos == 0)
+ {
+ if (_readPos < _readLength)
+ {
+ FlushReadBuffer();
+ }
+ _readPos = 0;
+ _readLength = 0;
+ }
+
+ // Determine how much space remains in the buffer
+ int remainingBuffer = _bufferLength - _writePos;
+ Debug.Assert(remainingBuffer >= 0);
+
+ // Simple/common case:
+ // - The write is smaller than our buffer, such that it's worth considering buffering it.
+ // - There's no active flush operation, such that we don't have to worry about the existing buffer being in use.
+ // - And the data we're trying to write fits in the buffer, meaning it wasn't already filled by previous writes.
+ // In that case, just store it in the buffer.
+ if (numBytes < _bufferLength && !HasActiveBufferOperation && numBytes <= remainingBuffer)
+ {
+ Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, numBytes);
+ _writePos += numBytes;
+ writeDataStoredInBuffer = true;
+
+ // There is one special-but-common case, common because devs often use
+ // byte[] sizes that are powers of 2 and thus fit nicely into our buffer, which is
+ // also a power of 2. If after our write the buffer still has remaining space,
+ // then we're done and can return a completed task now. But if we filled the buffer
+ // completely, we want to do the asynchronous flush/write as part of this operation
+ // rather than waiting until the next write that fills the buffer.
+ if (numBytes != remainingBuffer)
+ return Task.CompletedTask;
+
+ Debug.Assert(_writePos == _bufferLength);
+ }
+ }
+
+ // At this point, at least one of the following is true:
+ // 1. There was an active flush operation (it could have completed by now, though).
+ // 2. The data doesn't fit in the remaining buffer (or it's a pipe and we chose not to try).
+ // 3. We wrote all of the data to the buffer, filling it.
+ //
+ // If there's an active operation, we can't touch the current buffer because it's in use.
+ // That gives us a choice: we can either allocate a new buffer, or we can skip the buffer
+ // entirely (even if the data would otherwise fit in it). For now, for simplicity, we do
+ // the latter; it could also have performance wins due to OS-level optimizations, and we could
+ // potentially add support for PreAllocatedOverlapped due to having a single buffer. (We can
+ // switch to allocating a new buffer, potentially experimenting with buffer pooling, should
+ // performance data suggest it's appropriate.)
+ //
+ // If the data doesn't fit in the remaining buffer, it could be because it's so large
+ // it's greater than the entire buffer size, in which case we'd always skip the buffer,
+ // or it could be because there's more data than just the space remaining. For the latter
+ // case, we need to issue an asynchronous write to flush that data, which then turns this into
+ // the first case above with an active operation.
+ //
+ // If we already stored the data, then we have nothing additional to write beyond what
+ // we need to flush.
+ //
+ // In any of these cases, we have the same outcome:
+ // - If there's data in the buffer, flush it by writing it out asynchronously.
+ // - Then, if there's any data to be written, issue a write for it concurrently.
+ // We return a Task that represents one or both.
+
+ // Flush the buffer asynchronously if there's anything to flush
+ Task flushTask = null;
+ if (_writePos > 0)
+ {
+ flushTask = FlushWriteAsync(cancellationToken);
+
+ // If we already copied all of the data into the buffer,
+ // simply return the flush task here. Same goes for if the task has
+ // already completed and was unsuccessful.
+ if (writeDataStoredInBuffer ||
+ flushTask.IsFaulted ||
+ flushTask.IsCanceled)
+ {
+ return flushTask;
+ }
+ }
+
+ Debug.Assert(!writeDataStoredInBuffer);
+ Debug.Assert(_writePos == 0);
+
+ // Finally, issue the write asynchronously, and return a Task that logically
+ // represents the write operation, including any flushing done.
+ Task writeTask = WriteInternalCoreAsync(array, offset, numBytes, cancellationToken);
+ return
+ (flushTask == null || flushTask.Status == TaskStatus.RanToCompletion) ? writeTask :
+ (writeTask.Status == TaskStatus.RanToCompletion) ? flushTask :
+ Task.WhenAll(flushTask, writeTask);
+ }
+
+ private unsafe Task WriteInternalCoreAsync(byte[] bytes, int offset, int numBytes, CancellationToken cancellationToken)
+ {
+ Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
+ Debug.Assert(CanWrite, "_parent.CanWrite");
+ Debug.Assert(bytes != null, "bytes != null");
+ Debug.Assert(_readPos == _readLength, "_readPos == _readLen");
+ Debug.Assert(_useAsyncIO, "WriteInternalCoreAsync doesn't work on synchronous file streams!");
+ Debug.Assert(offset >= 0, "offset is negative");
+ Debug.Assert(numBytes >= 0, "numBytes is negative");
+
+ // Create and store async stream class library specific data in the async result
+ FileStreamCompletionSource completionSource = new FileStreamCompletionSource(this, 0, bytes, cancellationToken);
+ NativeOverlapped* intOverlapped = completionSource.Overlapped;
+
+ if (CanSeek)
+ {
+ // Make sure we set the length of the file appropriately.
+ long len = Length;
+ //Console.WriteLine("WriteInternalCoreAsync - Calculating end pos. pos: "+pos+" len: "+len+" numBytes: "+numBytes);
+
+ // Make sure we are writing to the position that we think we are
+ VerifyOSHandlePosition();
+
+ if (_filePosition + numBytes > len)
+ {
+ //Console.WriteLine("WriteInternalCoreAsync - Setting length to: "+(pos + numBytes));
+ SetLengthCore(_filePosition + numBytes);
+ }
+
+ // Now set the position to read from in the NativeOverlapped struct
+ // For pipes, we should leave the offset fields set to 0.
+ intOverlapped->OffsetLow = (int)_filePosition;
+ intOverlapped->OffsetHigh = (int)(_filePosition >> 32);
+
+ // When using overlapped IO, the OS is not supposed to
+ // touch the file pointer location at all. We will adjust it
+ // ourselves. This isn't threadsafe.
+ SeekCore(numBytes, SeekOrigin.Current);
+ }
+
+ //Console.WriteLine("WriteInternalCoreAsync finishing. pos: "+pos+" numBytes: "+numBytes+" _pos: "+_pos+" Position: "+Position);
+
+ int errorCode = 0;
+ // queue an async WriteFile operation and pass in a packed overlapped
+ int r = WriteFileNative(_fileHandle, bytes, offset, numBytes, intOverlapped, out errorCode);
+
+ // WriteFile, the OS version, will return 0 on failure. But
+ // my WriteFileNative wrapper returns -1. My wrapper will return
+ // the following:
+ // On error, r==-1.
+ // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
+ // On async requests that completed sequentially, r==0
+ // You will NEVER RELIABLY be able to get the number of bytes
+ // written back from this call when using overlapped IO! You must
+ // not pass in a non-null lpNumBytesWritten to WriteFile when using
+ // overlapped structures! This is ByDesign NT behavior.
+ if (r == -1 && numBytes != -1)
+ {
+ //Console.WriteLine("WriteFile returned 0; Write will complete asynchronously (if errorCode==3e5) errorCode: 0x{0:x}", errorCode);
+
+ // For pipes, when they are closed on the other side, they will come here.
+ if (errorCode == ERROR_NO_DATA)
+ {
+ // Not an error, but EOF. AsyncFSCallback will NOT be called.
+ // Completing TCS and return cached task allowing the GC to collect TCS.
+ completionSource.SetCompletedSynchronously(0);
+ return Task.CompletedTask;
+ }
+ else if (errorCode != ERROR_IO_PENDING)
+ {
+ if (!_fileHandle.IsClosed && CanSeek) // Update Position - It could be anywhere.
+ {
+ SeekCore(0, SeekOrigin.Current);
+ }
+
+ completionSource.ReleaseNativeResource();
+
+ if (errorCode == ERROR_HANDLE_EOF)
+ {
+ throw Error.GetEndOfFile();
+ }
+ else
+ {
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode);
+ }
+ }
+ else // ERROR_IO_PENDING
+ {
+ // Only once the IO is pending do we register for cancellation
+ completionSource.RegisterForCancellation();
+ }
+ }
+ else
+ {
+ // Due to a workaround for a race condition in NT's ReadFile &
+ // WriteFile routines, we will always be returning 0 from WriteFileNative
+ // when we do async IO instead of the number of bytes written,
+ // irregardless of whether the operation completed
+ // synchronously or asynchronously. We absolutely must not
+ // set asyncResult._numBytes here, since will never have correct
+ // results.
+ //Console.WriteLine("WriteFile returned: "+r+" (0x"+Int32.Format(r, "x")+") The IO completed synchronously, but the user callback was called on another thread.");
+ }
+
+ return completionSource.Task;
+ }
+
+ public override void WriteByte(byte value)
+ {
+ WriteByteCore(value);
+ }
+
+ // Windows API definitions, from winbase.h and others
+
+ private const int FILE_ATTRIBUTE_NORMAL = 0x00000080;
+ private const int FILE_ATTRIBUTE_ENCRYPTED = 0x00004000;
+ private const int FILE_FLAG_OVERLAPPED = 0x40000000;
+ internal const int GENERIC_READ = unchecked((int)0x80000000);
+ private const int GENERIC_WRITE = 0x40000000;
+
+ private const int FILE_BEGIN = 0;
+ private const int FILE_CURRENT = 1;
+ private const int FILE_END = 2;
+
+ // Error codes (not HRESULTS), from winerror.h
+ internal const int ERROR_BROKEN_PIPE = 109;
+ internal const int ERROR_NO_DATA = 232;
+ private const int ERROR_HANDLE_EOF = 38;
+ private const int ERROR_INVALID_PARAMETER = 87;
+ private const int ERROR_IO_PENDING = 997;
+
+ // __ConsoleStream also uses this code.
+ private unsafe int ReadFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int errorCode)
+ {
+ Debug.Assert(handle != null, "handle != null");
+ Debug.Assert(offset >= 0, "offset >= 0");
+ Debug.Assert(count >= 0, "count >= 0");
+ Debug.Assert(bytes != null, "bytes != null");
+ // Don't corrupt memory when multiple threads are erroneously writing
+ // to this stream simultaneously.
+ if (bytes.Length - offset < count)
+ throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition);
+
+ Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to ReadFileNative.");
+
+ // You can't use the fixed statement on an array of length 0.
+ if (bytes.Length == 0)
+ {
+ errorCode = 0;
+ return 0;
+ }
+
+ int r = 0;
+ int numBytesRead = 0;
+
+ fixed (byte* p = &bytes[0])
+ {
+ if (_useAsyncIO)
+ r = Interop.Kernel32.ReadFile(handle, p + offset, count, IntPtr.Zero, overlapped);
+ else
+ r = Interop.Kernel32.ReadFile(handle, p + offset, count, out numBytesRead, IntPtr.Zero);
+ }
+
+ if (r == 0)
+ {
+ errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid();
+ return -1;
+ }
+ else
+ {
+ errorCode = 0;
+ return numBytesRead;
+ }
+ }
+
+ private unsafe int WriteFileNative(SafeFileHandle handle, byte[] bytes, int offset, int count, NativeOverlapped* overlapped, out int errorCode)
+ {
+ Debug.Assert(handle != null, "handle != null");
+ Debug.Assert(offset >= 0, "offset >= 0");
+ Debug.Assert(count >= 0, "count >= 0");
+ Debug.Assert(bytes != null, "bytes != null");
+ // Don't corrupt memory when multiple threads are erroneously writing
+ // to this stream simultaneously. (the OS is reading from
+ // the array we pass to WriteFile, but if we read beyond the end and
+ // that memory isn't allocated, we could get an AV.)
+ if (bytes.Length - offset < count)
+ throw new IndexOutOfRangeException(SR.IndexOutOfRange_IORaceCondition);
+
+ Debug.Assert((_useAsyncIO && overlapped != null) || (!_useAsyncIO && overlapped == null), "Async IO and overlapped parameters inconsistent in call to WriteFileNative.");
+
+ // You can't use the fixed statement on an array of length 0.
+ if (bytes.Length == 0)
+ {
+ errorCode = 0;
+ return 0;
+ }
+
+ int numBytesWritten = 0;
+ int r = 0;
+
+ fixed (byte* p = &bytes[0])
+ {
+ if (_useAsyncIO)
+ r = Interop.Kernel32.WriteFile(handle, p + offset, count, IntPtr.Zero, overlapped);
+ else
+ r = Interop.Kernel32.WriteFile(handle, p + offset, count, out numBytesWritten, IntPtr.Zero);
+ }
+
+ if (r == 0)
+ {
+ errorCode = GetLastWin32ErrorAndDisposeHandleIfInvalid();
+ return -1;
+ }
+ else
+ {
+ errorCode = 0;
+ return numBytesWritten;
+ }
+ }
+
+ private int GetLastWin32ErrorAndDisposeHandleIfInvalid(bool throwIfInvalidHandle = false)
+ {
+ int errorCode = Marshal.GetLastWin32Error();
+
+ // If ERROR_INVALID_HANDLE is returned, it doesn't suffice to set
+ // the handle as invalid; the handle must also be closed.
+ //
+ // Marking the handle as invalid but not closing the handle
+ // resulted in exceptions during finalization and locked column
+ // values (due to invalid but unclosed handle) in SQL Win32FileStream
+ // scenarios.
+ //
+ // A more mainstream scenario involves accessing a file on a
+ // network share. ERROR_INVALID_HANDLE may occur because the network
+ // connection was dropped and the server closed the handle. However,
+ // the client side handle is still open and even valid for certain
+ // operations.
+ //
+ // Note that _parent.Dispose doesn't throw so we don't need to special case.
+ // SetHandleAsInvalid only sets _closed field to true (without
+ // actually closing handle) so we don't need to call that as well.
+ if (errorCode == Interop.Errors.ERROR_INVALID_HANDLE)
+ {
+ _fileHandle.Dispose();
+
+ if (throwIfInvalidHandle)
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode);
+ }
+
+ return errorCode;
+ }
+
+ public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+ {
+ // If we're in sync mode, just use the shared CopyToAsync implementation that does
+ // typical read/write looping. We also need to take this path if this is a derived
+ // instance from FileStream, as a derived type could have overridden ReadAsync, in which
+ // case our custom CopyToAsync implementation isn't necessarily correct.
+ if (!_useAsyncIO || GetType() != typeof(FileStream))
+ {
+ return base.CopyToAsync(destination, bufferSize, cancellationToken);
+ }
+
+ StreamHelpers.ValidateCopyToArgs(this, destination, bufferSize);
+
+ // Bail early for cancellation if cancellation has been requested
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled<int>(cancellationToken);
+ }
+
+ // Fail if the file was closed
+ if (_fileHandle.IsClosed)
+ {
+ throw Error.GetFileNotOpen();
+ }
+
+ // Do the async copy, with differing implementations based on whether the FileStream was opened as async or sync
+ Debug.Assert((_readPos == 0 && _readLength == 0 && _writePos >= 0) || (_writePos == 0 && _readPos <= _readLength), "We're either reading or writing, but not both.");
+ return AsyncModeCopyToAsync(destination, bufferSize, cancellationToken);
+ }
+
+ private async Task AsyncModeCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
+ {
+ Debug.Assert(_useAsyncIO, "This implementation is for async mode only");
+ Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");
+ Debug.Assert(CanRead, "_parent.CanRead");
+
+ // Make sure any pending writes have been flushed before we do a read.
+ if (_writePos > 0)
+ {
+ await FlushWriteAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ // Typically CopyToAsync would be invoked as the only "read" on the stream, but it's possible some reading is
+ // done and then the CopyToAsync is issued. For that case, see if we have any data available in the buffer.
+ if (GetBuffer() != null)
+ {
+ int bufferedBytes = _readLength - _readPos;
+ if (bufferedBytes > 0)
+ {
+ await destination.WriteAsync(GetBuffer(), _readPos, bufferedBytes, cancellationToken).ConfigureAwait(false);
+ _readPos = _readLength = 0;
+ }
+ }
+
+ // For efficiency, we avoid creating a new task and associated state for each asynchronous read.
+ // Instead, we create a single reusable awaitable object that will be triggered when an await completes
+ // and reset before going again.
+ var readAwaitable = new AsyncCopyToAwaitable(this);
+
+ // Make sure we are reading from the position that we think we are.
+ // Only set the position in the awaitable if we can seek (e.g. not for pipes).
+ bool canSeek = CanSeek;
+ if (canSeek)
+ {
+ VerifyOSHandlePosition();
+ readAwaitable._position = _filePosition;
+ }
+
+ // Get the buffer to use for the copy operation, as the base CopyToAsync does. We don't try to use
+ // _buffer here, even if it's not null, as concurrent operations are allowed, and another operation may
+ // actually be using the buffer already. Plus, it'll be rare for _buffer to be non-null, as typically
+ // CopyToAsync is used as the only operation performed on the stream, and the buffer is lazily initialized.
+ // Further, typically the CopyToAsync buffer size will be larger than that used by the FileStream, such that
+ // we'd likely be unable to use it anyway. Instead, we rent the buffer from a pool.
+ byte[] copyBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
+ bufferSize = 0; // repurpose bufferSize to be the high water mark for the buffer, to avoid an extra field in the state machine
+
+ // Allocate an Overlapped we can use repeatedly for all operations
+ var awaitableOverlapped = new PreAllocatedOverlapped(AsyncCopyToAwaitable.s_callback, readAwaitable, copyBuffer);
+ var cancellationReg = default(CancellationTokenRegistration);
+ try
+ {
+ // Register for cancellation. We do this once for the whole copy operation, and just try to cancel
+ // whatever read operation may currently be in progress, if there is one. It's possible the cancellation
+ // request could come in between operations, in which case we flag that with explicit calls to ThrowIfCancellationRequested
+ // in the read/write copy loop.
+ if (cancellationToken.CanBeCanceled)
+ {
+ cancellationReg = cancellationToken.Register(s =>
+ {
+ var innerAwaitable = (AsyncCopyToAwaitable)s;
+ unsafe
+ {
+ lock (innerAwaitable.CancellationLock) // synchronize with cleanup of the overlapped
+ {
+ if (innerAwaitable._nativeOverlapped != null)
+ {
+ // Try to cancel the I/O. We ignore the return value, as cancellation is opportunistic and we
+ // don't want to fail the operation because we couldn't cancel it.
+ Interop.Kernel32.CancelIoEx(innerAwaitable._fileStream._fileHandle, innerAwaitable._nativeOverlapped);
+ }
+ }
+ }
+ }, readAwaitable);
+ }
+
+ // Repeatedly read from this FileStream and write the results to the destination stream.
+ while (true)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ readAwaitable.ResetForNextOperation();
+
+ try
+ {
+ bool synchronousSuccess;
+ int errorCode;
+ unsafe
+ {
+ // Allocate a native overlapped for our reusable overlapped, and set position to read based on the next
+ // desired address stored in the awaitable. (This position may be 0, if either we're at the beginning or
+ // if the stream isn't seekable.)
+ readAwaitable._nativeOverlapped = _fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(awaitableOverlapped);
+ if (canSeek)
+ {
+ readAwaitable._nativeOverlapped->OffsetLow = unchecked((int)readAwaitable._position);
+ readAwaitable._nativeOverlapped->OffsetHigh = (int)(readAwaitable._position >> 32);
+ }
+
+ // Kick off the read.
+ synchronousSuccess = ReadFileNative(_fileHandle, copyBuffer, 0, copyBuffer.Length, readAwaitable._nativeOverlapped, out errorCode) >= 0;
+ }
+
+ // If the operation did not synchronously succeed, it either failed or initiated the asynchronous operation.
+ if (!synchronousSuccess)
+ {
+ switch (errorCode)
+ {
+ case ERROR_IO_PENDING:
+ // Async operation in progress.
+ break;
+ case ERROR_BROKEN_PIPE:
+ case ERROR_HANDLE_EOF:
+ // We're at or past the end of the file, and the overlapped callback
+ // won't be raised in these cases. Mark it as completed so that the await
+ // below will see it as such.
+ readAwaitable.MarkCompleted();
+ break;
+ default:
+ // Everything else is an error (and there won't be a callback).
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode);
+ }
+ }
+
+ // Wait for the async operation (which may or may not have already completed), then throw if it failed.
+ await readAwaitable;
+ switch (readAwaitable._errorCode)
+ {
+ case 0: // success
+ Debug.Assert(readAwaitable._numBytes >= 0, $"Expected non-negative numBytes, got {readAwaitable._numBytes}");
+ break;
+ case ERROR_BROKEN_PIPE: // logically success with 0 bytes read (write end of pipe closed)
+ case ERROR_HANDLE_EOF: // logically success with 0 bytes read (read at end of file)
+ Debug.Assert(readAwaitable._numBytes == 0, $"Expected 0 bytes read, got {readAwaitable._numBytes}");
+ break;
+ case Interop.Errors.ERROR_OPERATION_ABORTED: // canceled
+ throw new OperationCanceledException(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true));
+ default: // error
+ throw Win32Marshal.GetExceptionForWin32Error((int)readAwaitable._errorCode);
+ }
+
+ // Successful operation. If we got zero bytes, we're done: exit the read/write loop.
+ int numBytesRead = (int)readAwaitable._numBytes;
+ if (numBytesRead == 0)
+ {
+ break;
+ }
+
+ // Otherwise, update the read position for next time accordingly.
+ if (canSeek)
+ {
+ readAwaitable._position += numBytesRead;
+ }
+
+ // (and keep track of the maximum number of bytes in the buffer we used, to avoid excessive and unnecessary
+ // clearing of the buffer before we return it to the pool)
+ if (numBytesRead > bufferSize)
+ {
+ bufferSize = numBytesRead;
+ }
+ }
+ finally
+ {
+ // Free the resources for this read operation
+ unsafe
+ {
+ NativeOverlapped* overlapped;
+ lock (readAwaitable.CancellationLock) // just an Exchange, but we need this to be synchronized with cancellation, so using the same lock
+ {
+ overlapped = readAwaitable._nativeOverlapped;
+ readAwaitable._nativeOverlapped = null;
+ }
+ if (overlapped != null)
+ {
+ _fileHandle.ThreadPoolBinding.FreeNativeOverlapped(overlapped);
+ }
+ }
+ }
+
+ // Write out the read data.
+ await destination.WriteAsync(copyBuffer, 0, (int)readAwaitable._numBytes, cancellationToken).ConfigureAwait(false);
+ }
+ }
+ finally
+ {
+ // Cleanup from the whole copy operation
+ cancellationReg.Dispose();
+ awaitableOverlapped.Dispose();
+
+ Array.Clear(copyBuffer, 0, bufferSize);
+ ArrayPool<byte>.Shared.Return(copyBuffer, clearArray: false);
+
+ // Make sure the stream's current position reflects where we ended up
+ if (!_fileHandle.IsClosed && CanSeek)
+ {
+ SeekCore(0, SeekOrigin.End);
+ }
+ }
+ }
+
+ /// <summary>Used by CopyToAsync to enable awaiting the result of an overlapped I/O operation with minimal overhead.</summary>
+ private sealed unsafe class AsyncCopyToAwaitable : ICriticalNotifyCompletion
+ {
+ /// <summary>Sentinel object used to indicate that the I/O operation has completed before being awaited.</summary>
+ private readonly static Action s_sentinel = () => { };
+ /// <summary>Cached delegate to IOCallback.</summary>
+ internal static readonly IOCompletionCallback s_callback = IOCallback;
+
+ /// <summary>The FileStream that owns this instance.</summary>
+ internal readonly FileStream _fileStream;
+
+ /// <summary>Tracked position representing the next location from which to read.</summary>
+ internal long _position;
+ /// <summary>The current native overlapped pointer. This changes for each operation.</summary>
+ internal NativeOverlapped* _nativeOverlapped;
+ /// <summary>
+ /// null if the operation is still in progress,
+ /// s_sentinel if the I/O operation completed before the await,
+ /// s_callback if it completed after the await yielded.
+ /// </summary>
+ internal Action _continuation;
+ /// <summary>Last error code from completed operation.</summary>
+ internal uint _errorCode;
+ /// <summary>Last number of read bytes from completed operation.</summary>
+ internal uint _numBytes;
+
+ /// <summary>Lock object used to protect cancellation-related access to _nativeOverlapped.</summary>
+ internal object CancellationLock => this;
+
+ /// <summary>Initialize the awaitable.</summary>
+ internal unsafe AsyncCopyToAwaitable(FileStream fileStream)
+ {
+ _fileStream = fileStream;
+ }
+
+ /// <summary>Reset state to prepare for the next read operation.</summary>
+ internal void ResetForNextOperation()
+ {
+ Debug.Assert(_position >= 0, $"Expected non-negative position, got {_position}");
+ _continuation = null;
+ _errorCode = 0;
+ _numBytes = 0;
+ }
+
+ /// <summary>Overlapped callback: store the results, then invoke the continuation delegate.</summary>
+ internal unsafe static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOVERLAP)
+ {
+ var awaitable = (AsyncCopyToAwaitable)ThreadPoolBoundHandle.GetNativeOverlappedState(pOVERLAP);
+
+ Debug.Assert(awaitable._continuation != s_sentinel, "Sentinel must not have already been set as the continuation");
+ awaitable._errorCode = errorCode;
+ awaitable._numBytes = numBytes;
+
+ (awaitable._continuation ?? Interlocked.CompareExchange(ref awaitable._continuation, s_sentinel, null))?.Invoke();
+ }
+
+ /// <summary>
+ /// Called when it's known that the I/O callback for an operation will not be invoked but we'll
+ /// still be awaiting the awaitable.
+ /// </summary>
+ internal void MarkCompleted()
+ {
+ Debug.Assert(_continuation == null, "Expected null continuation");
+ _continuation = s_sentinel;
+ }
+
+ public AsyncCopyToAwaitable GetAwaiter() => this;
+ public bool IsCompleted => _continuation == s_sentinel;
+ public void GetResult() { }
+ public void OnCompleted(Action continuation) => UnsafeOnCompleted(continuation);
+ public void UnsafeOnCompleted(Action continuation)
+ {
+ if (_continuation == s_sentinel ||
+ Interlocked.CompareExchange(ref _continuation, continuation, null) != null)
+ {
+ Debug.Assert(_continuation == s_sentinel, $"Expected continuation set to s_sentinel, got ${_continuation}");
+ Task.Run(continuation);
+ }
+ }
+ }
+
+ // Unlike Flush(), FlushAsync() always flushes to disk. This is intentional.
+ // Legend is that we chose not to flush the OS file buffers in Flush() in fear of
+ // perf problems with frequent, long running FlushFileBuffers() calls. But we don't
+ // have that problem with FlushAsync() because we will call FlushFileBuffers() in the background.
+ private Task FlushAsyncInternal(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled(cancellationToken);
+
+ if (_fileHandle.IsClosed)
+ throw Error.GetFileNotOpen();
+
+ // The always synchronous data transfer between the OS and the internal buffer is intentional
+ // because this is needed to allow concurrent async IO requests. Concurrent data transfer
+ // between the OS and the internal buffer will result in race conditions. Since FlushWrite and
+ // FlushRead modify internal state of the stream and transfer data between the OS and the
+ // internal buffer, they cannot be truly async. We will, however, flush the OS file buffers
+ // asynchronously because it doesn't modify any internal state of the stream and is potentially
+ // a long running process.
+ try
+ {
+ FlushInternalBuffer();
+ }
+ catch (Exception e)
+ {
+ return Task.FromException(e);
+ }
+
+ if (CanWrite)
+ {
+ return Task.Factory.StartNew(
+ state => ((FileStream)state).FlushOSBuffer(),
+ this,
+ cancellationToken,
+ TaskCreationOptions.DenyChildAttach,
+ TaskScheduler.Default);
+ }
+ else
+ {
+ return Task.CompletedTask;
+ }
+ }
+
+ private Task<int> TaskFromResultOrCache(int result)
+ {
+ Task<int> completedTask = _lastSynchronouslyCompletedTask;
+ Debug.Assert(completedTask == null || completedTask.Status == TaskStatus.RanToCompletion, "Cached task should have completed successfully");
+
+ if ((completedTask == null) || (completedTask.Result != result))
+ {
+ completedTask = Task.FromResult(result);
+ _lastSynchronouslyCompletedTask = completedTask;
+ }
+
+ return completedTask;
+ }
+
+ private void LockInternal(long position, long length)
+ {
+ int positionLow = unchecked((int)(position));
+ int positionHigh = unchecked((int)(position >> 32));
+ int lengthLow = unchecked((int)(length));
+ int lengthHigh = unchecked((int)(length >> 32));
+
+ if (!Interop.Kernel32.LockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh))
+ {
+ throw Win32Marshal.GetExceptionForLastWin32Error();
+ }
+ }
+
+ private void UnlockInternal(long position, long length)
+ {
+ int positionLow = unchecked((int)(position));
+ int positionHigh = unchecked((int)(position >> 32));
+ int lengthLow = unchecked((int)(length));
+ int lengthHigh = unchecked((int)(length >> 32));
+
+ if (!Interop.Kernel32.UnlockFile(_fileHandle, positionLow, positionHigh, lengthLow, lengthHigh))
+ {
+ throw Win32Marshal.GetExceptionForLastWin32Error();
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileStream.cs b/src/mscorlib/shared/System/IO/FileStream.cs
new file mode 100644
index 0000000000..7db8518435
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileStream.cs
@@ -0,0 +1,684 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Threading;
+using System.Threading.Tasks;
+using Microsoft.Win32.SafeHandles;
+using System.Diagnostics;
+
+namespace System.IO
+{
+ public partial class FileStream : Stream
+ {
+ private const FileShare DefaultShare = FileShare.Read;
+ private const bool DefaultIsAsync = false;
+ internal const int DefaultBufferSize = 4096;
+
+ private byte[] _buffer;
+ private int _bufferLength;
+ private readonly SafeFileHandle _fileHandle;
+
+ /// <summary>Whether the file is opened for reading, writing, or both.</summary>
+ private readonly FileAccess _access;
+
+ /// <summary>The path to the opened file.</summary>
+ private readonly string _path;
+
+ /// <summary>The next available byte to be read from the _buffer.</summary>
+ private int _readPos;
+
+ /// <summary>The number of valid bytes in _buffer.</summary>
+ private int _readLength;
+
+ /// <summary>The next location in which a write should occur to the buffer.</summary>
+ private int _writePos;
+
+ /// <summary>
+ /// Whether asynchronous read/write/flush operations should be performed using async I/O.
+ /// On Windows FileOptions.Asynchronous controls how the file handle is configured,
+ /// and then as a result how operations are issued against that file handle. On Unix,
+ /// there isn't any distinction around how file descriptors are created for async vs
+ /// sync, but we still differentiate how the operations are issued in order to provide
+ /// similar behavioral semantics and performance characteristics as on Windows. On
+ /// Windows, if non-async, async read/write requests just delegate to the base stream,
+ /// and no attempt is made to synchronize between sync and async operations on the stream;
+ /// if async, then async read/write requests are implemented specially, and sync read/write
+ /// requests are coordinated with async ones by implementing the sync ones over the async
+ /// ones. On Unix, we do something similar. If non-async, async read/write requests just
+ /// delegate to the base stream, and no attempt is made to synchronize. If async, we use
+ /// a semaphore to coordinate both sync and async operations.
+ /// </summary>
+ private readonly bool _useAsyncIO;
+
+ /// <summary>
+ /// Currently cached position in the stream. This should always mirror the underlying file's actual position,
+ /// and should only ever be out of sync if another stream with access to this same file manipulates it, at which
+ /// point we attempt to error out.
+ /// </summary>
+ private long _filePosition;
+
+ /// <summary>Whether the file stream's handle has been exposed.</summary>
+ private bool _exposedHandle;
+
+ [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
+ public FileStream(IntPtr handle, FileAccess access)
+ : this(handle, access, true, DefaultBufferSize, false)
+ {
+ }
+
+ [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")]
+ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle)
+ : this(handle, access, ownsHandle, DefaultBufferSize, false)
+ {
+ }
+
+ [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")]
+ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
+ : this(handle, access, ownsHandle, bufferSize, false)
+ {
+ }
+
+ [Obsolete("This constructor has been deprecated. Please use new FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")]
+ public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
+ : this(new SafeFileHandle(handle, ownsHandle), access, bufferSize, isAsync)
+ {
+ }
+
+ public FileStream(SafeFileHandle handle, FileAccess access)
+ : this(handle, access, DefaultBufferSize)
+ {
+ }
+
+ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize)
+ : this(handle, access, bufferSize, GetDefaultIsAsync(handle))
+ {
+ }
+
+ public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
+ {
+ if (handle.IsInvalid)
+ throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle));
+
+ if (access < FileAccess.Read || access > FileAccess.ReadWrite)
+ throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum);
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+
+ if (handle.IsClosed)
+ throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed);
+ if (handle.IsAsync.HasValue && isAsync != handle.IsAsync.Value)
+ throw new ArgumentException(SR.Arg_HandleNotAsync, nameof(handle));
+
+ _access = access;
+ _useAsyncIO = isAsync;
+ _exposedHandle = true;
+ _bufferLength = bufferSize;
+ _fileHandle = handle;
+
+ InitFromHandle(handle);
+ }
+
+ public FileStream(string path, FileMode mode) :
+ this(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), DefaultShare, DefaultBufferSize, DefaultIsAsync)
+ { }
+
+ public FileStream(string path, FileMode mode, FileAccess access) :
+ this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync)
+ { }
+
+ public FileStream(string path, FileMode mode, FileAccess access, FileShare share) :
+ this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync)
+ { }
+
+ public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) :
+ this(path, mode, access, share, bufferSize, DefaultIsAsync)
+ { }
+
+ public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) :
+ this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
+ { }
+
+ public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path), SR.ArgumentNull_Path);
+ if (path.Length == 0)
+ throw new ArgumentException(SR.Argument_EmptyPath, nameof(path));
+
+ // don't include inheritable in our bounds check for share
+ FileShare tempshare = share & ~FileShare.Inheritable;
+ string badArg = null;
+
+ if (mode < FileMode.CreateNew || mode > FileMode.Append)
+ badArg = nameof(mode);
+ else if (access < FileAccess.Read || access > FileAccess.ReadWrite)
+ badArg = nameof(access);
+ else if (tempshare < FileShare.None || tempshare > (FileShare.ReadWrite | FileShare.Delete))
+ badArg = nameof(share);
+
+ if (badArg != null)
+ throw new ArgumentOutOfRangeException(badArg, SR.ArgumentOutOfRange_Enum);
+
+ // NOTE: any change to FileOptions enum needs to be matched here in the error validation
+ if (options != FileOptions.None && (options & ~(FileOptions.WriteThrough | FileOptions.Asynchronous | FileOptions.RandomAccess | FileOptions.DeleteOnClose | FileOptions.SequentialScan | FileOptions.Encrypted | (FileOptions)0x20000000 /* NoBuffering */)) != 0)
+ throw new ArgumentOutOfRangeException(nameof(options), SR.ArgumentOutOfRange_Enum);
+
+ if (bufferSize <= 0)
+ throw new ArgumentOutOfRangeException(nameof(bufferSize), SR.ArgumentOutOfRange_NeedPosNum);
+
+ // Write access validation
+ if ((access & FileAccess.Write) == 0)
+ {
+ if (mode == FileMode.Truncate || mode == FileMode.CreateNew || mode == FileMode.Create || mode == FileMode.Append)
+ {
+ // No write access, mode and access disagree but flag access since mode comes first
+ throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, mode, access), nameof(access));
+ }
+ }
+
+ if ((access & FileAccess.Read) != 0 && mode == FileMode.Append)
+ throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(access));
+
+ string fullPath = Path.GetFullPath(path);
+
+ _path = fullPath;
+ _access = access;
+ _bufferLength = bufferSize;
+
+ if ((options & FileOptions.Asynchronous) != 0)
+ _useAsyncIO = true;
+
+ _fileHandle = OpenHandle(mode, share, options);
+
+ try
+ {
+ Init(mode, share);
+ }
+ catch
+ {
+ // If anything goes wrong while setting up the stream, make sure we deterministically dispose
+ // of the opened handle.
+ _fileHandle.Dispose();
+ _fileHandle = null;
+ throw;
+ }
+ }
+
+ private static bool GetDefaultIsAsync(SafeFileHandle handle)
+ {
+ // This will eventually get more complicated as we can actually check the underlying handle type on Windows
+ return handle.IsAsync.HasValue ? handle.IsAsync.Value : false;
+ }
+
+ [Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. http://go.microsoft.com/fwlink/?linkid=14202")]
+ public virtual IntPtr Handle { get { return SafeFileHandle.DangerousGetHandle(); } }
+
+ public virtual void Lock(long position, long length)
+ {
+ if (position < 0 || length < 0)
+ {
+ throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (_fileHandle.IsClosed)
+ {
+ throw Error.GetFileNotOpen();
+ }
+
+ LockInternal(position, length);
+ }
+
+ public virtual void Unlock(long position, long length)
+ {
+ if (position < 0 || length < 0)
+ {
+ throw new ArgumentOutOfRangeException(position < 0 ? nameof(position) : nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+
+ if (_fileHandle.IsClosed)
+ {
+ throw Error.GetFileNotOpen();
+ }
+
+ UnlockInternal(position, length);
+ }
+
+ public override Task FlushAsync(CancellationToken cancellationToken)
+ {
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Flush() which a subclass might have overridden. To be safe
+ // we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Flush) when we are not sure.
+ if (GetType() != typeof(FileStream))
+ return base.FlushAsync(cancellationToken);
+
+ return FlushAsyncInternal(cancellationToken);
+ }
+
+ public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ if (buffer == null)
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (buffer.Length - offset < count)
+ throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
+
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Read() or ReadAsync() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Read/ReadAsync) when we are not sure.
+ if (GetType() != typeof(FileStream))
+ return base.ReadAsync(buffer, offset, count, cancellationToken);
+
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled<int>(cancellationToken);
+
+ if (IsClosed)
+ throw Error.GetFileNotOpen();
+
+ return ReadAsyncInternal(buffer, offset, count, cancellationToken);
+ }
+
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ if (buffer == null)
+ throw new ArgumentNullException(nameof(buffer), SR.ArgumentNull_Buffer);
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (buffer.Length - offset < count)
+ throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
+
+ // If we have been inherited into a subclass, the following implementation could be incorrect
+ // since it does not call through to Write() or WriteAsync() which a subclass might have overridden.
+ // To be safe we will only use this implementation in cases where we know it is safe to do so,
+ // and delegate to our base class (which will call into Write/WriteAsync) when we are not sure.
+ if (GetType() != typeof(FileStream))
+ return base.WriteAsync(buffer, offset, count, cancellationToken);
+
+ if (cancellationToken.IsCancellationRequested)
+ return Task.FromCanceled(cancellationToken);
+
+ if (IsClosed)
+ throw Error.GetFileNotOpen();
+
+ return WriteAsyncInternal(buffer, offset, count, cancellationToken);
+ }
+
+ /// <summary>
+ /// Clears buffers for this stream and causes any buffered data to be written to the file.
+ /// </summary>
+ public override void Flush()
+ {
+ // Make sure that we call through the public virtual API
+ Flush(flushToDisk: false);
+ }
+
+ /// <summary>
+ /// Clears buffers for this stream, and if <param name="flushToDisk"/> is true,
+ /// causes any buffered data to be written to the file.
+ /// </summary>
+ public virtual void Flush(bool flushToDisk)
+ {
+ if (IsClosed) throw Error.GetFileNotOpen();
+
+ FlushInternalBuffer();
+
+ if (flushToDisk && CanWrite)
+ {
+ FlushOSBuffer();
+ }
+ }
+
+ /// <summary>Gets a value indicating whether the current stream supports reading.</summary>
+ public override bool CanRead
+ {
+ get { return !_fileHandle.IsClosed && (_access & FileAccess.Read) != 0; }
+ }
+
+ /// <summary>Gets a value indicating whether the current stream supports writing.</summary>
+ public override bool CanWrite
+ {
+ get { return !_fileHandle.IsClosed && (_access & FileAccess.Write) != 0; }
+ }
+
+ /// <summary>Validates arguments to Read and Write and throws resulting exceptions.</summary>
+ /// <param name="array">The buffer to read from or write to.</param>
+ /// <param name="offset">The zero-based offset into the array.</param>
+ /// <param name="count">The maximum number of bytes to read or write.</param>
+ private void ValidateReadWriteArgs(byte[] array, int offset, int count)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array), SR.ArgumentNull_Buffer);
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (array.Length - offset < count)
+ throw new ArgumentException(SR.Argument_InvalidOffLen /*, no good single parameter name to pass*/);
+ if (_fileHandle.IsClosed)
+ throw Error.GetFileNotOpen();
+ }
+
+ /// <summary>Sets the length of this stream to the given value.</summary>
+ /// <param name="value">The new length of the stream.</param>
+ public override void SetLength(long value)
+ {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (_fileHandle.IsClosed)
+ throw Error.GetFileNotOpen();
+ if (!CanSeek)
+ throw Error.GetSeekNotSupported();
+ if (!CanWrite)
+ throw Error.GetWriteNotSupported();
+
+ SetLengthInternal(value);
+ }
+
+ public virtual SafeFileHandle SafeFileHandle
+ {
+ get
+ {
+ Flush();
+ _exposedHandle = true;
+ return _fileHandle;
+ }
+ }
+
+ /// <summary>Gets the path that was passed to the constructor.</summary>
+ public virtual string Name { get { return _path ?? SR.IO_UnknownFileName; } }
+
+ /// <summary>Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously.</summary>
+ public virtual bool IsAsync
+ {
+ get { return _useAsyncIO; }
+ }
+
+ /// <summary>Gets the length of the stream in bytes.</summary>
+ public override long Length
+ {
+ get
+ {
+ if (_fileHandle.IsClosed) throw Error.GetFileNotOpen();
+ if (!CanSeek) throw Error.GetSeekNotSupported();
+ return GetLengthInternal();
+ }
+ }
+
+ /// <summary>
+ /// Verify that the actual position of the OS's handle equals what we expect it to.
+ /// This will fail if someone else moved the UnixFileStream's handle or if
+ /// our position updating code is incorrect.
+ /// </summary>
+ private void VerifyOSHandlePosition()
+ {
+ bool verifyPosition = _exposedHandle; // in release, only verify if we've given out the handle such that someone else could be manipulating it
+#if DEBUG
+ verifyPosition = true; // in debug, always make sure our position matches what the OS says it should be
+#endif
+ if (verifyPosition && CanSeek)
+ {
+ long oldPos = _filePosition; // SeekCore will override the current _position, so save it now
+ long curPos = SeekCore(0, SeekOrigin.Current);
+ if (oldPos != curPos)
+ {
+ // For reads, this is non-fatal but we still could have returned corrupted
+ // data in some cases, so discard the internal buffer. For writes,
+ // this is a problem; discard the buffer and error out.
+ _readPos = _readLength = 0;
+ if (_writePos > 0)
+ {
+ _writePos = 0;
+ throw new IOException(SR.IO_FileStreamHandlePosition);
+ }
+ }
+ }
+ }
+
+ /// <summary>Verifies that state relating to the read/write buffer is consistent.</summary>
+ [Conditional("DEBUG")]
+ private void AssertBufferInvariants()
+ {
+ // Read buffer values must be in range: 0 <= _bufferReadPos <= _bufferReadLength <= _bufferLength
+ Debug.Assert(0 <= _readPos && _readPos <= _readLength && _readLength <= _bufferLength);
+
+ // Write buffer values must be in range: 0 <= _bufferWritePos <= _bufferLength
+ Debug.Assert(0 <= _writePos && _writePos <= _bufferLength);
+
+ // Read buffering and write buffering can't both be active
+ Debug.Assert((_readPos == 0 && _readLength == 0) || _writePos == 0);
+ }
+
+ /// <summary>Validates that we're ready to read from the stream.</summary>
+ private void PrepareForReading()
+ {
+ if (_fileHandle.IsClosed)
+ throw Error.GetFileNotOpen();
+ if (_readLength == 0 && !CanRead)
+ throw Error.GetReadNotSupported();
+
+ AssertBufferInvariants();
+ }
+
+ /// <summary>Gets or sets the position within the current stream</summary>
+ public override long Position
+ {
+ get
+ {
+ if (_fileHandle.IsClosed)
+ throw Error.GetFileNotOpen();
+
+ if (!CanSeek)
+ throw Error.GetSeekNotSupported();
+
+ AssertBufferInvariants();
+ VerifyOSHandlePosition();
+
+ // We may have read data into our buffer from the handle, such that the handle position
+ // is artificially further along than the consumer's view of the stream's position.
+ // Thus, when reading, our position is really starting from the handle position negatively
+ // offset by the number of bytes in the buffer and positively offset by the number of
+ // bytes into that buffer we've read. When writing, both the read length and position
+ // must be zero, and our position is just the handle position offset positive by how many
+ // bytes we've written into the buffer.
+ return (_filePosition - _readLength) + _readPos + _writePos;
+ }
+ set
+ {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ Seek(value, SeekOrigin.Begin);
+ }
+ }
+
+ internal virtual bool IsClosed => _fileHandle.IsClosed;
+
+ /// <summary>
+ /// Gets the array used for buffering reading and writing.
+ /// If the array hasn't been allocated, this will lazily allocate it.
+ /// </summary>
+ /// <returns>The buffer.</returns>
+ private byte[] GetBuffer()
+ {
+ Debug.Assert(_buffer == null || _buffer.Length == _bufferLength);
+ if (_buffer == null)
+ {
+ _buffer = new byte[_bufferLength];
+ OnBufferAllocated();
+ }
+
+ return _buffer;
+ }
+
+ partial void OnBufferAllocated();
+
+ /// <summary>
+ /// Flushes the internal read/write buffer for this stream. If write data has been buffered,
+ /// that data is written out to the underlying file. Or if data has been buffered for
+ /// reading from the stream, the data is dumped and our position in the underlying file
+ /// is rewound as necessary. This does not flush the OS buffer.
+ /// </summary>
+ private void FlushInternalBuffer()
+ {
+ AssertBufferInvariants();
+ if (_writePos > 0)
+ {
+ FlushWriteBuffer();
+ }
+ else if (_readPos < _readLength && CanSeek)
+ {
+ FlushReadBuffer();
+ }
+ }
+
+ /// <summary>Dumps any read data in the buffer and rewinds our position in the stream, accordingly, as necessary.</summary>
+ private void FlushReadBuffer()
+ {
+ // Reading is done by blocks from the file, but someone could read
+ // 1 byte from the buffer then write. At that point, the OS's file
+ // pointer is out of sync with the stream's position. All write
+ // functions should call this function to preserve the position in the file.
+
+ AssertBufferInvariants();
+ Debug.Assert(_writePos == 0, "FileStream: Write buffer must be empty in FlushReadBuffer!");
+
+ int rewind = _readPos - _readLength;
+ if (rewind != 0)
+ {
+ Debug.Assert(CanSeek, "FileStream will lose buffered read data now.");
+ SeekCore(rewind, SeekOrigin.Current);
+ }
+ _readPos = _readLength = 0;
+ }
+
+ private int ReadByteCore()
+ {
+ PrepareForReading();
+
+ byte[] buffer = GetBuffer();
+ if (_readPos == _readLength)
+ {
+ FlushWriteBuffer();
+ Debug.Assert(_bufferLength > 0, "_bufferSize > 0");
+
+ _readLength = ReadNative(buffer, 0, _bufferLength);
+ _readPos = 0;
+ if (_readLength == 0)
+ {
+ return -1;
+ }
+ }
+
+ return buffer[_readPos++];
+ }
+
+ private void WriteByteCore(byte value)
+ {
+ PrepareForWriting();
+
+ // Flush the write buffer if it's full
+ if (_writePos == _bufferLength)
+ FlushWriteBuffer();
+
+ // We now have space in the buffer. Store the byte.
+ GetBuffer()[_writePos++] = value;
+ }
+
+ /// <summary>
+ /// Validates that we're ready to write to the stream,
+ /// including flushing a read buffer if necessary.
+ /// </summary>
+ private void PrepareForWriting()
+ {
+ if (_fileHandle.IsClosed)
+ throw Error.GetFileNotOpen();
+
+ // Make sure we're good to write. We only need to do this if there's nothing already
+ // in our write buffer, since if there is something in the buffer, we've already done
+ // this checking and flushing.
+ if (_writePos == 0)
+ {
+ if (!CanWrite) throw Error.GetWriteNotSupported();
+ FlushReadBuffer();
+ Debug.Assert(_bufferLength > 0, "_bufferSize > 0");
+ }
+ }
+
+ ~FileStream()
+ {
+ // Preserved for compatibility since FileStream has defined a
+ // finalizer in past releases and derived classes may depend
+ // on Dispose(false) call.
+ Dispose(false);
+ }
+
+ public override IAsyncResult BeginRead(byte[] array, int offset, int numBytes, AsyncCallback callback, object state)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (numBytes < 0)
+ throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (array.Length - offset < numBytes)
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+
+ if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed);
+ if (!CanRead) throw new NotSupportedException(SR.NotSupported_UnreadableStream);
+
+ if (!IsAsync)
+ return base.BeginRead(array, offset, numBytes, callback, state);
+ else
+ return TaskToApm.Begin(ReadAsyncInternal(array, offset, numBytes, CancellationToken.None), callback, state);
+ }
+
+ public override IAsyncResult BeginWrite(byte[] array, int offset, int numBytes, AsyncCallback callback, object state)
+ {
+ if (array == null)
+ throw new ArgumentNullException(nameof(array));
+ if (offset < 0)
+ throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (numBytes < 0)
+ throw new ArgumentOutOfRangeException(nameof(numBytes), SR.ArgumentOutOfRange_NeedNonNegNum);
+ if (array.Length - offset < numBytes)
+ throw new ArgumentException(SR.Argument_InvalidOffLen);
+
+ if (IsClosed) throw new ObjectDisposedException(SR.ObjectDisposed_FileClosed);
+ if (!CanWrite) throw new NotSupportedException(SR.NotSupported_UnwritableStream);
+
+ if (!IsAsync)
+ return base.BeginWrite(array, offset, numBytes, callback, state);
+ else
+ return TaskToApm.Begin(WriteAsyncInternal(array, offset, numBytes, CancellationToken.None), callback, state);
+ }
+
+ public override int EndRead(IAsyncResult asyncResult)
+ {
+ if (asyncResult == null)
+ throw new ArgumentNullException(nameof(asyncResult));
+
+ if (!IsAsync)
+ return base.EndRead(asyncResult);
+ else
+ return TaskToApm.End<int>(asyncResult);
+ }
+
+ public override void EndWrite(IAsyncResult asyncResult)
+ {
+ if (asyncResult == null)
+ throw new ArgumentNullException(nameof(asyncResult));
+
+ if (!IsAsync)
+ base.EndWrite(asyncResult);
+ else
+ TaskToApm.End(asyncResult);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs b/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs
new file mode 100644
index 0000000000..7dca13335e
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/FileStreamCompletionSource.Win32.cs
@@ -0,0 +1,222 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Security;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+
+namespace System.IO
+{
+ public partial class FileStream : Stream
+ {
+ // This is an internal object extending TaskCompletionSource with fields
+ // for all of the relevant data necessary to complete the IO operation.
+ // This is used by IOCallback and all of the async methods.
+ unsafe private sealed class FileStreamCompletionSource : TaskCompletionSource<int>
+ {
+ private const long NoResult = 0;
+ private const long ResultSuccess = (long)1 << 32;
+ private const long ResultError = (long)2 << 32;
+ private const long RegisteringCancellation = (long)4 << 32;
+ private const long CompletedCallback = (long)8 << 32;
+ private const ulong ResultMask = ((ulong)uint.MaxValue) << 32;
+
+ private static Action<object> s_cancelCallback;
+
+ private readonly FileStream _stream;
+ private readonly int _numBufferedBytes;
+ private readonly CancellationToken _cancellationToken;
+ private CancellationTokenRegistration _cancellationRegistration;
+#if DEBUG
+ private bool _cancellationHasBeenRegistered;
+#endif
+ private NativeOverlapped* _overlapped; // Overlapped class responsible for operations in progress when an appdomain unload occurs
+ private long _result; // Using long since this needs to be used in Interlocked APIs
+
+ // Using RunContinuationsAsynchronously for compat reasons (old API used Task.Factory.StartNew for continuations)
+ internal FileStreamCompletionSource(FileStream stream, int numBufferedBytes, byte[] bytes, CancellationToken cancellationToken)
+ : base(TaskCreationOptions.RunContinuationsAsynchronously)
+ {
+ _numBufferedBytes = numBufferedBytes;
+ _stream = stream;
+ _result = NoResult;
+ _cancellationToken = cancellationToken;
+
+ // Create the native overlapped. We try to use the preallocated overlapped if possible:
+ // it's possible if the byte buffer is the same one that's associated with the preallocated overlapped
+ // and if no one else is currently using the preallocated overlapped. This is the fast-path for cases
+ // where the user-provided buffer is smaller than the FileStream's buffer (such that the FileStream's
+ // buffer is used) and where operations on the FileStream are not being performed concurrently.
+ _overlapped = ReferenceEquals(bytes, _stream._buffer) && _stream.CompareExchangeCurrentOverlappedOwner(this, null) == null ?
+ _stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(_stream._preallocatedOverlapped) :
+ _stream._fileHandle.ThreadPoolBinding.AllocateNativeOverlapped(s_ioCallback, this, bytes);
+ Debug.Assert(_overlapped != null, "AllocateNativeOverlapped returned null");
+ }
+
+ internal NativeOverlapped* Overlapped
+ {
+ get { return _overlapped; }
+ }
+
+ public void SetCompletedSynchronously(int numBytes)
+ {
+ ReleaseNativeResource();
+ TrySetResult(numBytes + _numBufferedBytes);
+ }
+
+ public void RegisterForCancellation()
+ {
+#if DEBUG
+ Debug.Assert(!_cancellationHasBeenRegistered, "Cannot register for cancellation twice");
+ _cancellationHasBeenRegistered = true;
+#endif
+
+ // Quick check to make sure that the cancellation token supports cancellation, and that the IO hasn't completed
+ if ((_cancellationToken.CanBeCanceled) && (_overlapped != null))
+ {
+ var cancelCallback = s_cancelCallback;
+ if (cancelCallback == null) s_cancelCallback = cancelCallback = Cancel;
+
+ // Register the cancellation only if the IO hasn't completed
+ long packedResult = Interlocked.CompareExchange(ref _result, RegisteringCancellation, NoResult);
+ if (packedResult == NoResult)
+ {
+ _cancellationRegistration = _cancellationToken.Register(cancelCallback, this);
+
+ // Switch the result, just in case IO completed while we were setting the registration
+ packedResult = Interlocked.Exchange(ref _result, NoResult);
+ }
+ else if (packedResult != CompletedCallback)
+ {
+ // Failed to set the result, IO is in the process of completing
+ // Attempt to take the packed result
+ packedResult = Interlocked.Exchange(ref _result, NoResult);
+ }
+
+ // If we have a callback that needs to be completed
+ if ((packedResult != NoResult) && (packedResult != CompletedCallback) && (packedResult != RegisteringCancellation))
+ {
+ CompleteCallback((ulong)packedResult);
+ }
+ }
+ }
+
+ internal void ReleaseNativeResource()
+ {
+ // Ensure that cancellation has been completed and cleaned up.
+ _cancellationRegistration.Dispose();
+
+ // Free the overlapped.
+ // NOTE: The cancellation must *NOT* be running at this point, or it may observe freed memory
+ // (this is why we disposed the registration above).
+ if (_overlapped != null)
+ {
+ _stream._fileHandle.ThreadPoolBinding.FreeNativeOverlapped(_overlapped);
+ _overlapped = null;
+ }
+
+ // Ensure we're no longer set as the current completion source (we may not have been to begin with).
+ // Only one operation at a time is eligible to use the preallocated overlapped,
+ _stream.CompareExchangeCurrentOverlappedOwner(null, this);
+ }
+
+ // When doing IO asynchronously (i.e. _isAsync==true), this callback is
+ // called by a free thread in the threadpool when the IO operation
+ // completes.
+ internal static unsafe void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped)
+ {
+ // Extract the completion source from the overlapped. The state in the overlapped
+ // will either be a Win32FileStream (in the case where the preallocated overlapped was used),
+ // in which case the operation being completed is its _currentOverlappedOwner, or it'll
+ // be directly the FileStreamCompletion that's completing (in the case where the preallocated
+ // overlapped was already in use by another operation).
+ object state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped);
+ FileStream fs = state as FileStream;
+ FileStreamCompletionSource completionSource = fs != null ?
+ fs._currentOverlappedOwner :
+ (FileStreamCompletionSource)state;
+ Debug.Assert(completionSource._overlapped == pOverlapped, "Overlaps don't match");
+
+ // Handle reading from & writing to closed pipes. While I'm not sure
+ // this is entirely necessary anymore, maybe it's possible for
+ // an async read on a pipe to be issued and then the pipe is closed,
+ // returning this error. This may very well be necessary.
+ ulong packedResult;
+ if (errorCode != 0 && errorCode != ERROR_BROKEN_PIPE && errorCode != ERROR_NO_DATA)
+ {
+ packedResult = ((ulong)ResultError | errorCode);
+ }
+ else
+ {
+ packedResult = ((ulong)ResultSuccess | numBytes);
+ }
+
+ // Stow the result so that other threads can observe it
+ // And, if no other thread is registering cancellation, continue
+ if (NoResult == Interlocked.Exchange(ref completionSource._result, (long)packedResult))
+ {
+ // Successfully set the state, attempt to take back the callback
+ if (Interlocked.Exchange(ref completionSource._result, CompletedCallback) != NoResult)
+ {
+ // Successfully got the callback, finish the callback
+ completionSource.CompleteCallback(packedResult);
+ }
+ // else: Some other thread stole the result, so now it is responsible to finish the callback
+ }
+ // else: Some other thread is registering a cancellation, so it *must* finish the callback
+ }
+
+ private void CompleteCallback(ulong packedResult)
+ {
+ // Free up the native resource and cancellation registration
+ ReleaseNativeResource();
+
+ // Unpack the result and send it to the user
+ long result = (long)(packedResult & ResultMask);
+ if (result == ResultError)
+ {
+ int errorCode = unchecked((int)(packedResult & uint.MaxValue));
+ if (errorCode == Interop.Errors.ERROR_OPERATION_ABORTED)
+ {
+ TrySetCanceled(_cancellationToken.IsCancellationRequested ? _cancellationToken : new CancellationToken(true));
+ }
+ else
+ {
+ TrySetException(Win32Marshal.GetExceptionForWin32Error(errorCode));
+ }
+ }
+ else
+ {
+ Debug.Assert(result == ResultSuccess, "Unknown result");
+ TrySetResult((int)(packedResult & uint.MaxValue) + _numBufferedBytes);
+ }
+ }
+
+ private static void Cancel(object state)
+ {
+ // WARNING: This may potentially be called under a lock (during cancellation registration)
+
+ FileStreamCompletionSource completionSource = state as FileStreamCompletionSource;
+ Debug.Assert(completionSource != null, "Unknown state passed to cancellation");
+ Debug.Assert(completionSource._overlapped != null && !completionSource.Task.IsCompleted, "IO should not have completed yet");
+
+ // If the handle is still valid, attempt to cancel the IO
+ if (!completionSource._stream._fileHandle.IsInvalid &&
+ !Interop.Kernel32.CancelIoEx(completionSource._stream._fileHandle, completionSource._overlapped))
+ {
+ int errorCode = Marshal.GetLastWin32Error();
+
+ // ERROR_NOT_FOUND is returned if CancelIoEx cannot find the request to cancel.
+ // This probably means that the IO operation has completed.
+ if (errorCode != Interop.Errors.ERROR_NOT_FOUND)
+ {
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/Path.Unix.cs b/src/mscorlib/shared/System/IO/Path.Unix.cs
new file mode 100644
index 0000000000..500c60aa8c
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/Path.Unix.cs
@@ -0,0 +1,215 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace System.IO
+{
+ public static partial class Path
+ {
+ public static char[] GetInvalidFileNameChars() => new char[] { '\0', '/' };
+
+ public static char[] GetInvalidPathChars() => new char[] { '\0' };
+
+ internal static int MaxPath => Interop.Sys.MaxPath;
+
+ // Expands the given path to a fully qualified path.
+ public static string GetFullPath(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+
+ if (path.Length == 0)
+ throw new ArgumentException(SR.Arg_PathIllegal);
+
+ PathInternal.CheckInvalidPathChars(path);
+
+ // Expand with current directory if necessary
+ if (!IsPathRooted(path))
+ {
+ path = Combine(Interop.Sys.GetCwd(), path);
+ }
+
+ // We would ideally use realpath to do this, but it resolves symlinks, requires that the file actually exist,
+ // and turns it into a full path, which we only want if fullCheck is true.
+ string collapsedString = RemoveRelativeSegments(path);
+
+ Debug.Assert(collapsedString.Length < path.Length || collapsedString.ToString() == path,
+ "Either we've removed characters, or the string should be unmodified from the input path.");
+
+ if (collapsedString.Length > Interop.Sys.MaxPath)
+ {
+ throw new PathTooLongException(SR.IO_PathTooLong);
+ }
+
+ string result = collapsedString.Length == 0 ? PathInternal.DirectorySeparatorCharAsString : collapsedString;
+
+ return result;
+ }
+
+ /// <summary>
+ /// Try to remove relative segments from the given path (without combining with a root).
+ /// </summary>
+ /// <param name="skip">Skip the specified number of characters before evaluating.</param>
+ private static string RemoveRelativeSegments(string path, int skip = 0)
+ {
+ bool flippedSeparator = false;
+
+ // Remove "//", "/./", and "/../" from the path by copying each character to the output,
+ // except the ones we're removing, such that the builder contains the normalized path
+ // at the end.
+ var sb = StringBuilderCache.Acquire(path.Length);
+ if (skip > 0)
+ {
+ sb.Append(path, 0, skip);
+ }
+
+ int componentCharCount = 0;
+ for (int i = skip; i < path.Length; i++)
+ {
+ char c = path[i];
+
+ if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length)
+ {
+ componentCharCount = 0;
+
+ // Skip this character if it's a directory separator and if the next character is, too,
+ // e.g. "parent//child" => "parent/child"
+ if (PathInternal.IsDirectorySeparator(path[i + 1]))
+ {
+ continue;
+ }
+
+ // Skip this character and the next if it's referring to the current directory,
+ // e.g. "parent/./child" =? "parent/child"
+ if ((i + 2 == path.Length || PathInternal.IsDirectorySeparator(path[i + 2])) &&
+ path[i + 1] == '.')
+ {
+ i++;
+ continue;
+ }
+
+ // Skip this character and the next two if it's referring to the parent directory,
+ // e.g. "parent/child/../grandchild" => "parent/grandchild"
+ if (i + 2 < path.Length &&
+ (i + 3 == path.Length || PathInternal.IsDirectorySeparator(path[i + 3])) &&
+ path[i + 1] == '.' && path[i + 2] == '.')
+ {
+ // Unwind back to the last slash (and if there isn't one, clear out everything).
+ int s;
+ for (s = sb.Length - 1; s >= 0; s--)
+ {
+ if (PathInternal.IsDirectorySeparator(sb[s]))
+ {
+ sb.Length = s;
+ break;
+ }
+ }
+ if (s < 0)
+ {
+ sb.Length = 0;
+ }
+
+ i += 2;
+ continue;
+ }
+ }
+
+ if (++componentCharCount > Interop.Sys.MaxName)
+ {
+ throw new PathTooLongException(SR.IO_PathTooLong);
+ }
+
+ // Normalize the directory separator if needed
+ if (c != PathInternal.DirectorySeparatorChar && c == PathInternal.AltDirectorySeparatorChar)
+ {
+ c = PathInternal.DirectorySeparatorChar;
+ flippedSeparator = true;
+ }
+
+ sb.Append(c);
+ }
+
+ if (flippedSeparator || sb.Length != path.Length)
+ {
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+ else
+ {
+ // We haven't changed the source path, return the original
+ StringBuilderCache.Release(sb);
+ return path;
+ }
+ }
+
+ private static string RemoveLongPathPrefix(string path)
+ {
+ return path; // nop. There's nothing special about "long" paths on Unix.
+ }
+
+ public static string GetTempPath()
+ {
+ const string TempEnvVar = "TMPDIR";
+ const string DefaultTempPath = "/tmp/";
+
+ // Get the temp path from the TMPDIR environment variable.
+ // If it's not set, just return the default path.
+ // If it is, return it, ensuring it ends with a slash.
+ string path = Environment.GetEnvironmentVariable(TempEnvVar);
+ return
+ string.IsNullOrEmpty(path) ? DefaultTempPath :
+ PathInternal.IsDirectorySeparator(path[path.Length - 1]) ? path :
+ path + PathInternal.DirectorySeparatorChar;
+ }
+
+ public static string GetTempFileName()
+ {
+ const string Suffix = ".tmp";
+ const int SuffixByteLength = 4;
+
+ // mkstemps takes a char* and overwrites the XXXXXX with six characters
+ // that'll result in a unique file name.
+ string template = GetTempPath() + "tmpXXXXXX" + Suffix + "\0";
+ byte[] name = Encoding.UTF8.GetBytes(template);
+
+ // Create, open, and close the temp file.
+ IntPtr fd = Interop.CheckIo(Interop.Sys.MksTemps(name, SuffixByteLength));
+ Interop.Sys.Close(fd); // ignore any errors from close; nothing to do if cleanup isn't possible
+
+ // 'name' is now the name of the file
+ Debug.Assert(name[name.Length - 1] == '\0');
+ return Encoding.UTF8.GetString(name, 0, name.Length - 1); // trim off the trailing '\0'
+ }
+
+ public static bool IsPathRooted(string path)
+ {
+ if (path == null)
+ return false;
+
+ PathInternal.CheckInvalidPathChars(path);
+ return path.Length > 0 && path[0] == PathInternal.DirectorySeparatorChar;
+ }
+
+ public static string GetPathRoot(string path)
+ {
+ if (path == null) return null;
+ return IsPathRooted(path) ? PathInternal.DirectorySeparatorCharAsString : String.Empty;
+ }
+
+ /// <summary>Gets whether the system is case-sensitive.</summary>
+ internal static bool IsCaseSensitive
+ {
+ get
+ {
+ #if PLATFORM_OSX
+ return false;
+ #else
+ return true;
+ #endif
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/Path.Windows.cs b/src/mscorlib/shared/System/IO/Path.Windows.cs
new file mode 100644
index 0000000000..d6f0c628c3
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/Path.Windows.cs
@@ -0,0 +1,155 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Text;
+
+namespace System.IO
+{
+ public static partial class Path
+ {
+ public static char[] GetInvalidFileNameChars() => new char[]
+ {
+ '\"', '<', '>', '|', '\0',
+ (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
+ (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
+ (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
+ (char)31, ':', '*', '?', '\\', '/'
+ };
+
+ public static char[] GetInvalidPathChars() => new char[]
+ {
+ '|', '\0',
+ (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10,
+ (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20,
+ (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30,
+ (char)31
+ };
+
+ // The max total path is 260, and the max individual component length is 255.
+ // For example, D:\<256 char file name> isn't legal, even though it's under 260 chars.
+ internal const int MaxPath = 260;
+
+ // Expands the given path to a fully qualified path.
+ public static string GetFullPath(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+
+ // Embedded null characters are the only invalid character case we want to check up front.
+ // This is because the nulls will signal the end of the string to Win32 and therefore have
+ // unpredictable results. Other invalid characters we give a chance to be normalized out.
+ if (path.IndexOf('\0') != -1)
+ throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
+
+ if (PathInternal.IsExtended(path))
+ {
+ // We can't really know what is valid for all cases of extended paths.
+ //
+ // - object names can include other characters as well (':', '/', etc.)
+ // - even file objects have different rules (pipe names can contain most characters)
+ //
+ // As such we will do no further analysis of extended paths to avoid blocking known and unknown
+ // scenarios as well as minimizing compat breaks should we block now and need to unblock later.
+ return path;
+ }
+
+ bool isDevice = PathInternal.IsDevice(path);
+ if (!isDevice)
+ {
+ // Toss out paths with colons that aren't a valid drive specifier.
+ // Cannot start with a colon and can only be of the form "C:".
+ // (Note that we used to explicitly check "http:" and "file:"- these are caught by this check now.)
+ int startIndex = PathInternal.PathStartSkip(path);
+
+ // Move past the colon
+ startIndex += 2;
+
+ if ((path.Length > 0 && path[0] == PathInternal.VolumeSeparatorChar)
+ || (path.Length >= startIndex && path[startIndex - 1] == PathInternal.VolumeSeparatorChar && !PathInternal.IsValidDriveChar(path[startIndex - 2]))
+ || (path.Length > startIndex && path.IndexOf(PathInternal.VolumeSeparatorChar, startIndex) != -1))
+ {
+ throw new NotSupportedException(SR.Argument_PathFormatNotSupported);
+ }
+ }
+
+ // Technically this doesn't matter but we used to throw for this case
+ if (string.IsNullOrWhiteSpace(path))
+ throw new ArgumentException(SR.Arg_PathIllegal);
+
+ // We don't want to check invalid characters for device format- see comments for extended above
+ string fullPath = PathHelper.Normalize(path, checkInvalidCharacters: !isDevice, expandShortPaths: true);
+
+ if (!isDevice)
+ {
+ // Emulate FileIOPermissions checks, retained for compatibility (normal invalid characters have already been checked)
+ if (PathInternal.HasWildCardCharacters(fullPath))
+ throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
+ }
+
+ return fullPath;
+ }
+
+ public static string GetTempPath()
+ {
+ StringBuilder sb = StringBuilderCache.Acquire(MaxPath);
+ uint r = Interop.Kernel32.GetTempPathW(MaxPath, sb);
+ if (r == 0)
+ throw Win32Marshal.GetExceptionForLastWin32Error();
+ return GetFullPath(StringBuilderCache.GetStringAndRelease(sb));
+ }
+
+ // Returns a unique temporary file name, and creates a 0-byte file by that
+ // name on disk.
+ public static string GetTempFileName()
+ {
+ string path = GetTempPath();
+
+ StringBuilder sb = StringBuilderCache.Acquire(MaxPath);
+ uint r = Interop.Kernel32.GetTempFileNameW(path, "tmp", 0, sb);
+ if (r == 0)
+ throw Win32Marshal.GetExceptionForLastWin32Error();
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+
+ // Tests if the given path contains a root. A path is considered rooted
+ // if it starts with a backslash ("\") or a valid drive letter and a colon (":").
+ public static bool IsPathRooted(string path)
+ {
+ if (path != null)
+ {
+ PathInternal.CheckInvalidPathChars(path);
+
+ int length = path.Length;
+ if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])) ||
+ (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar))
+ return true;
+ }
+ return false;
+ }
+
+ // Returns the root portion of the given path. The resulting string
+ // consists of those rightmost characters of the path that constitute the
+ // root of the path. Possible patterns for the resulting string are: An
+ // empty string (a relative path on the current drive), "\" (an absolute
+ // path on the current drive), "X:" (a relative path on a given drive,
+ // where X is the drive letter), "X:\" (an absolute path on a given drive),
+ // and "\\server\share" (a UNC path for a given server and share name).
+ // The resulting string is null if path is null.
+ public static string GetPathRoot(string path)
+ {
+ if (path == null) return null;
+ PathInternal.CheckInvalidPathChars(path);
+
+ // Need to return the normalized directory separator
+ path = PathInternal.NormalizeDirectorySeparators(path);
+
+ int pathRoot = PathInternal.GetRootLength(path);
+ return pathRoot <= 0 ? string.Empty : path.Substring(0, pathRoot);
+ }
+
+ /// <summary>Gets whether the system is case-sensitive.</summary>
+ internal static bool IsCaseSensitive { get { return false; } }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/Path.cs b/src/mscorlib/shared/System/IO/Path.cs
new file mode 100644
index 0000000000..b3a8783c32
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/Path.cs
@@ -0,0 +1,574 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Text;
+
+namespace System.IO
+{
+ // Provides methods for processing file system strings in a cross-platform manner.
+ // Most of the methods don't do a complete parsing (such as examining a UNC hostname),
+ // but they will handle most string operations.
+ public static partial class Path
+ {
+ // Public static readonly variant of the separators. The Path implementation itself is using
+ // internal const variant of the separators for better performance.
+ public static readonly char DirectorySeparatorChar = PathInternal.DirectorySeparatorChar;
+ public static readonly char AltDirectorySeparatorChar = PathInternal.AltDirectorySeparatorChar;
+ public static readonly char VolumeSeparatorChar = PathInternal.VolumeSeparatorChar;
+ public static readonly char PathSeparator = PathInternal.PathSeparator;
+
+ // For generating random file names
+ // 8 random bytes provides 12 chars in our encoding for the 8.3 name.
+ private const int KeyLength = 8;
+
+ [Obsolete("Please use GetInvalidPathChars or GetInvalidFileNameChars instead.")]
+ public static readonly char[] InvalidPathChars = GetInvalidPathChars();
+
+ // Changes the extension of a file path. The path parameter
+ // specifies a file path, and the extension parameter
+ // specifies a file extension (with a leading period, such as
+ // ".exe" or ".cs").
+ //
+ // The function returns a file path with the same root, directory, and base
+ // name parts as path, but with the file extension changed to
+ // the specified extension. If path is null, the function
+ // returns null. If path does not contain a file extension,
+ // the new file extension is appended to the path. If extension
+ // is null, any existing extension is removed from path.
+ public static string ChangeExtension(string path, string extension)
+ {
+ if (path != null)
+ {
+ PathInternal.CheckInvalidPathChars(path);
+
+ string s = path;
+ for (int i = path.Length - 1; i >= 0; i--)
+ {
+ char ch = path[i];
+ if (ch == '.')
+ {
+ s = path.Substring(0, i);
+ break;
+ }
+ if (PathInternal.IsDirectoryOrVolumeSeparator(ch)) break;
+ }
+
+ if (extension != null && path.Length != 0)
+ {
+ s = (extension.Length == 0 || extension[0] != '.') ?
+ s + "." + extension :
+ s + extension;
+ }
+
+ return s;
+ }
+ return null;
+ }
+
+ // Returns the directory path of a file path. This method effectively
+ // removes the last element of the given file path, i.e. it returns a
+ // string consisting of all characters up to but not including the last
+ // backslash ("\") in the file path. The returned value is null if the file
+ // path is null or if the file path denotes a root (such as "\", "C:", or
+ // "\\server\share").
+ public static string GetDirectoryName(string path)
+ {
+ if (path != null)
+ {
+ PathInternal.CheckInvalidPathChars(path);
+ path = PathInternal.NormalizeDirectorySeparators(path);
+ int root = PathInternal.GetRootLength(path);
+
+ int i = path.Length;
+ if (i > root)
+ {
+ while (i > root && !PathInternal.IsDirectorySeparator(path[--i])) ;
+ return path.Substring(0, i);
+ }
+ }
+ return null;
+ }
+
+ // Returns the extension of the given path. The returned value includes the
+ // period (".") character of the extension except when you have a terminal period when you get string.Empty, such as ".exe" or
+ // ".cpp". The returned value is null if the given path is
+ // null or if the given path does not include an extension.
+ [Pure]
+ public static string GetExtension(string path)
+ {
+ if (path == null)
+ return null;
+
+ PathInternal.CheckInvalidPathChars(path);
+ int length = path.Length;
+ for (int i = length - 1; i >= 0; i--)
+ {
+ char ch = path[i];
+ if (ch == '.')
+ {
+ if (i != length - 1)
+ return path.Substring(i, length - i);
+ else
+ return string.Empty;
+ }
+ if (PathInternal.IsDirectoryOrVolumeSeparator(ch))
+ break;
+ }
+ return string.Empty;
+ }
+
+ // Returns the name and extension parts of the given path. The resulting
+ // string contains the characters of path that follow the last
+ // separator in path. The resulting string is null if path is null.
+ [Pure]
+ public static string GetFileName(string path)
+ {
+ if (path == null)
+ return null;
+
+ int offset = PathInternal.FindFileNameIndex(path);
+ int count = path.Length - offset;
+ return path.Substring(offset, count);
+ }
+
+ [Pure]
+ public static string GetFileNameWithoutExtension(string path)
+ {
+ if (path == null)
+ return null;
+
+ int length = path.Length;
+ int offset = PathInternal.FindFileNameIndex(path);
+
+ int end = path.LastIndexOf('.', length - 1, length - offset);
+ return end == -1 ?
+ path.Substring(offset) : // No extension was found
+ path.Substring(offset, end - offset);
+ }
+
+ // Returns a cryptographically strong random 8.3 string that can be
+ // used as either a folder name or a file name.
+ public static unsafe string GetRandomFileName()
+ {
+ byte* pKey = stackalloc byte[KeyLength];
+ Interop.GetRandomBytes(pKey, KeyLength);
+
+ const int RandomFileNameLength = 12;
+ char* pRandomFileName = stackalloc char[RandomFileNameLength];
+ Populate83FileNameFromRandomBytes(pKey, KeyLength, pRandomFileName, RandomFileNameLength);
+ return new string(pRandomFileName, 0, RandomFileNameLength);
+ }
+
+ // Tests if a path includes a file extension. The result is
+ // true if the characters that follow the last directory
+ // separator ('\\' or '/') or volume separator (':') in the path include
+ // a period (".") other than a terminal period. The result is false otherwise.
+ [Pure]
+ public static bool HasExtension(string path)
+ {
+ if (path != null)
+ {
+ PathInternal.CheckInvalidPathChars(path);
+
+ for (int i = path.Length - 1; i >= 0; i--)
+ {
+ char ch = path[i];
+ if (ch == '.')
+ {
+ return i != path.Length - 1;
+ }
+ if (PathInternal.IsDirectoryOrVolumeSeparator(ch)) break;
+ }
+ }
+ return false;
+ }
+
+ public static string Combine(string path1, string path2)
+ {
+ if (path1 == null || path2 == null)
+ throw new ArgumentNullException((path1 == null) ? nameof(path1) : nameof(path2));
+ Contract.EndContractBlock();
+
+ PathInternal.CheckInvalidPathChars(path1);
+ PathInternal.CheckInvalidPathChars(path2);
+
+ return CombineNoChecks(path1, path2);
+ }
+
+ public static string Combine(string path1, string path2, string path3)
+ {
+ if (path1 == null || path2 == null || path3 == null)
+ throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : nameof(path3));
+ Contract.EndContractBlock();
+
+ PathInternal.CheckInvalidPathChars(path1);
+ PathInternal.CheckInvalidPathChars(path2);
+ PathInternal.CheckInvalidPathChars(path3);
+
+ return CombineNoChecks(path1, path2, path3);
+ }
+
+ public static string Combine(string path1, string path2, string path3, string path4)
+ {
+ if (path1 == null || path2 == null || path3 == null || path4 == null)
+ throw new ArgumentNullException((path1 == null) ? nameof(path1) : (path2 == null) ? nameof(path2) : (path3 == null) ? nameof(path3) : nameof(path4));
+ Contract.EndContractBlock();
+
+ PathInternal.CheckInvalidPathChars(path1);
+ PathInternal.CheckInvalidPathChars(path2);
+ PathInternal.CheckInvalidPathChars(path3);
+ PathInternal.CheckInvalidPathChars(path4);
+
+ return CombineNoChecks(path1, path2, path3, path4);
+ }
+
+ public static string Combine(params string[] paths)
+ {
+ if (paths == null)
+ {
+ throw new ArgumentNullException(nameof(paths));
+ }
+ Contract.EndContractBlock();
+
+ int finalSize = 0;
+ int firstComponent = 0;
+
+ // We have two passes, the first calculates how large a buffer to allocate and does some precondition
+ // checks on the paths passed in. The second actually does the combination.
+
+ for (int i = 0; i < paths.Length; i++)
+ {
+ if (paths[i] == null)
+ {
+ throw new ArgumentNullException(nameof(paths));
+ }
+
+ if (paths[i].Length == 0)
+ {
+ continue;
+ }
+
+ PathInternal.CheckInvalidPathChars(paths[i]);
+
+ if (IsPathRooted(paths[i]))
+ {
+ firstComponent = i;
+ finalSize = paths[i].Length;
+ }
+ else
+ {
+ finalSize += paths[i].Length;
+ }
+
+ char ch = paths[i][paths[i].Length - 1];
+ if (!PathInternal.IsDirectoryOrVolumeSeparator(ch))
+ finalSize++;
+ }
+
+ StringBuilder finalPath = StringBuilderCache.Acquire(finalSize);
+
+ for (int i = firstComponent; i < paths.Length; i++)
+ {
+ if (paths[i].Length == 0)
+ {
+ continue;
+ }
+
+ if (finalPath.Length == 0)
+ {
+ finalPath.Append(paths[i]);
+ }
+ else
+ {
+ char ch = finalPath[finalPath.Length - 1];
+ if (!PathInternal.IsDirectoryOrVolumeSeparator(ch))
+ {
+ finalPath.Append(PathInternal.DirectorySeparatorChar);
+ }
+
+ finalPath.Append(paths[i]);
+ }
+ }
+
+ return StringBuilderCache.GetStringAndRelease(finalPath);
+ }
+
+ private static string CombineNoChecks(string path1, string path2)
+ {
+ if (path2.Length == 0)
+ return path1;
+
+ if (path1.Length == 0)
+ return path2;
+
+ if (IsPathRooted(path2))
+ return path2;
+
+ char ch = path1[path1.Length - 1];
+ return PathInternal.IsDirectoryOrVolumeSeparator(ch) ?
+ path1 + path2 :
+ path1 + PathInternal.DirectorySeparatorCharAsString + path2;
+ }
+
+ private static string CombineNoChecks(string path1, string path2, string path3)
+ {
+ if (path1.Length == 0)
+ return CombineNoChecks(path2, path3);
+ if (path2.Length == 0)
+ return CombineNoChecks(path1, path3);
+ if (path3.Length == 0)
+ return CombineNoChecks(path1, path2);
+
+ if (IsPathRooted(path3))
+ return path3;
+ if (IsPathRooted(path2))
+ return CombineNoChecks(path2, path3);
+
+ bool hasSep1 = PathInternal.IsDirectoryOrVolumeSeparator(path1[path1.Length - 1]);
+ bool hasSep2 = PathInternal.IsDirectoryOrVolumeSeparator(path2[path2.Length - 1]);
+
+ if (hasSep1 && hasSep2)
+ {
+ return path1 + path2 + path3;
+ }
+ else if (hasSep1)
+ {
+ return path1 + path2 + PathInternal.DirectorySeparatorCharAsString + path3;
+ }
+ else if (hasSep2)
+ {
+ return path1 + PathInternal.DirectorySeparatorCharAsString + path2 + path3;
+ }
+ else
+ {
+ // string.Concat only has string-based overloads up to four arguments; after that requires allocating
+ // a params string[]. Instead, try to use a cached StringBuilder.
+ StringBuilder sb = StringBuilderCache.Acquire(path1.Length + path2.Length + path3.Length + 2);
+ sb.Append(path1)
+ .Append(PathInternal.DirectorySeparatorChar)
+ .Append(path2)
+ .Append(PathInternal.DirectorySeparatorChar)
+ .Append(path3);
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+ }
+
+ private static string CombineNoChecks(string path1, string path2, string path3, string path4)
+ {
+ if (path1.Length == 0)
+ return CombineNoChecks(path2, path3, path4);
+ if (path2.Length == 0)
+ return CombineNoChecks(path1, path3, path4);
+ if (path3.Length == 0)
+ return CombineNoChecks(path1, path2, path4);
+ if (path4.Length == 0)
+ return CombineNoChecks(path1, path2, path3);
+
+ if (IsPathRooted(path4))
+ return path4;
+ if (IsPathRooted(path3))
+ return CombineNoChecks(path3, path4);
+ if (IsPathRooted(path2))
+ return CombineNoChecks(path2, path3, path4);
+
+ bool hasSep1 = PathInternal.IsDirectoryOrVolumeSeparator(path1[path1.Length - 1]);
+ bool hasSep2 = PathInternal.IsDirectoryOrVolumeSeparator(path2[path2.Length - 1]);
+ bool hasSep3 = PathInternal.IsDirectoryOrVolumeSeparator(path3[path3.Length - 1]);
+
+ if (hasSep1 && hasSep2 && hasSep3)
+ {
+ // Use string.Concat overload that takes four strings
+ return path1 + path2 + path3 + path4;
+ }
+ else
+ {
+ // string.Concat only has string-based overloads up to four arguments; after that requires allocating
+ // a params string[]. Instead, try to use a cached StringBuilder.
+ StringBuilder sb = StringBuilderCache.Acquire(path1.Length + path2.Length + path3.Length + path4.Length + 3);
+
+ sb.Append(path1);
+ if (!hasSep1)
+ {
+ sb.Append(PathInternal.DirectorySeparatorChar);
+ }
+
+ sb.Append(path2);
+ if (!hasSep2)
+ {
+ sb.Append(PathInternal.DirectorySeparatorChar);
+ }
+
+ sb.Append(path3);
+ if (!hasSep3)
+ {
+ sb.Append(PathInternal.DirectorySeparatorChar);
+ }
+
+ sb.Append(path4);
+
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+ }
+
+ private static readonly char[] s_base32Char = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', '0', '1', '2', '3', '4', '5'};
+
+ private static unsafe void Populate83FileNameFromRandomBytes(byte* bytes, int byteCount, char* chars, int charCount)
+ {
+ Debug.Assert(bytes != null);
+ Debug.Assert(chars != null);
+
+ // This method requires bytes of length 8 and chars of length 12.
+ Debug.Assert(byteCount == 8, $"Unexpected {nameof(byteCount)}");
+ Debug.Assert(charCount == 12, $"Unexpected {nameof(charCount)}");
+
+ byte b0 = bytes[0];
+ byte b1 = bytes[1];
+ byte b2 = bytes[2];
+ byte b3 = bytes[3];
+ byte b4 = bytes[4];
+
+ // Consume the 5 Least significant bits of the first 5 bytes
+ chars[0] = s_base32Char[b0 & 0x1F];
+ chars[1] = s_base32Char[b1 & 0x1F];
+ chars[2] = s_base32Char[b2 & 0x1F];
+ chars[3] = s_base32Char[b3 & 0x1F];
+ chars[4] = s_base32Char[b4 & 0x1F];
+
+ // Consume 3 MSB of b0, b1, MSB bits 6, 7 of b3, b4
+ chars[5] = s_base32Char[(
+ ((b0 & 0xE0) >> 5) |
+ ((b3 & 0x60) >> 2))];
+
+ chars[6] = s_base32Char[(
+ ((b1 & 0xE0) >> 5) |
+ ((b4 & 0x60) >> 2))];
+
+ // Consume 3 MSB bits of b2, 1 MSB bit of b3, b4
+ b2 >>= 5;
+
+ Debug.Assert(((b2 & 0xF8) == 0), "Unexpected set bits");
+
+ if ((b3 & 0x80) != 0)
+ b2 |= 0x08;
+ if ((b4 & 0x80) != 0)
+ b2 |= 0x10;
+
+ chars[7] = s_base32Char[b2];
+
+ // Set the file extension separator
+ chars[8] = '.';
+
+ // Consume the 5 Least significant bits of the remaining 3 bytes
+ chars[9] = s_base32Char[(bytes[5] & 0x1F)];
+ chars[10] = s_base32Char[(bytes[6] & 0x1F)];
+ chars[11] = s_base32Char[(bytes[7] & 0x1F)];
+ }
+
+ /// <summary>
+ /// Create a relative path from one path to another. Paths will be resolved before calculating the difference.
+ /// Default path comparison for the active platform will be used (OrdinalIgnoreCase for Windows or Mac, Ordinal for Unix).
+ /// </summary>
+ /// <param name="relativeTo">The source path the output should be relative to. This path is always considered to be a directory.</param>
+ /// <param name="path">The destination path.</param>
+ /// <returns>The relative path or <paramref name="path"/> if the paths don't share the same root.</returns>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="relativeTo"/> or <paramref name="path"/> is <c>null</c> or an empty string.</exception>
+ public static string GetRelativePath(string relativeTo, string path)
+ {
+ return GetRelativePath(relativeTo, path, StringComparison);
+ }
+
+ private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType)
+ {
+ if (string.IsNullOrEmpty(relativeTo)) throw new ArgumentNullException(nameof(relativeTo));
+ if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullException(nameof(path));
+ Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase);
+
+ relativeTo = GetFullPath(relativeTo);
+ path = GetFullPath(path);
+
+ // Need to check if the roots are different- if they are we need to return the "to" path.
+ if (!PathInternal.AreRootsEqual(relativeTo, path, comparisonType))
+ return path;
+
+ int commonLength = PathInternal.GetCommonPathLength(relativeTo, path, ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase);
+
+ // If there is nothing in common they can't share the same root, return the "to" path as is.
+ if (commonLength == 0)
+ return path;
+
+ // Trailing separators aren't significant for comparison
+ int relativeToLength = relativeTo.Length;
+ if (PathInternal.EndsInDirectorySeparator(relativeTo))
+ relativeToLength--;
+
+ bool pathEndsInSeparator = PathInternal.EndsInDirectorySeparator(path);
+ int pathLength = path.Length;
+ if (pathEndsInSeparator)
+ pathLength--;
+
+ // If we have effectively the same path, return "."
+ if (relativeToLength == pathLength && commonLength >= relativeToLength) return ".";
+
+ // We have the same root, we need to calculate the difference now using the
+ // common Length and Segment count past the length.
+ //
+ // Some examples:
+ //
+ // C:\Foo C:\Bar L3, S1 -> ..\Bar
+ // C:\Foo C:\Foo\Bar L6, S0 -> Bar
+ // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar
+ // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar
+
+ StringBuilder sb = StringBuilderCache.Acquire(Math.Max(relativeTo.Length, path.Length));
+
+ // Add parent segments for segments past the common on the "from" path
+ if (commonLength < relativeToLength)
+ {
+ sb.Append(PathInternal.ParentDirectoryPrefix);
+
+ for (int i = commonLength; i < relativeToLength; i++)
+ {
+ if (PathInternal.IsDirectorySeparator(relativeTo[i]))
+ {
+ sb.Append(PathInternal.ParentDirectoryPrefix);
+ }
+ }
+ }
+ else if (PathInternal.IsDirectorySeparator(path[commonLength]))
+ {
+ // No parent segments and we need to eat the initial separator
+ // (C:\Foo C:\Foo\Bar case)
+ commonLength++;
+ }
+
+ // Now add the rest of the "to" path, adding back the trailing separator
+ int count = pathLength - commonLength;
+ if (pathEndsInSeparator)
+ count++;
+
+ sb.Append(path, commonLength, count);
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+
+ // StringComparison and IsCaseSensitive are also available in PathInternal.CaseSensitivity but we are
+ // too low in System.Runtime.Extensions to use it (no FileStream, etc.)
+
+ /// <summary>Returns a comparison that can be used to compare file and directory names for equality.</summary>
+ internal static StringComparison StringComparison
+ {
+ get
+ {
+ return IsCaseSensitive ?
+ StringComparison.Ordinal :
+ StringComparison.OrdinalIgnoreCase;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/PathHelper.Windows.cs b/src/mscorlib/shared/System/IO/PathHelper.Windows.cs
new file mode 100644
index 0000000000..e2ead93185
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/PathHelper.Windows.cs
@@ -0,0 +1,398 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System.IO
+{
+ /// <summary>
+ /// Wrapper to help with path normalization.
+ /// </summary>
+ internal class PathHelper
+ {
+ // Can't be over 8.3 and be a short name
+ private const int MaxShortName = 12;
+
+ private const char LastAnsi = (char)255;
+ private const char Delete = (char)127;
+
+ /// <summary>
+ /// Normalize the given path.
+ /// </summary>
+ /// <remarks>
+ /// Normalizes via Win32 GetFullPathName(). It will also trim all "typical" whitespace at the end of the path (see s_trimEndChars). Will also trim initial
+ /// spaces if the path is determined to be rooted.
+ ///
+ /// Note that invalid characters will be checked after the path is normalized, which could remove bad characters. (C:\|\..\a.txt -- C:\a.txt)
+ /// </remarks>
+ /// <param name="path">Path to normalize</param>
+ /// <param name="checkInvalidCharacters">True to check for invalid characters</param>
+ /// <param name="expandShortPaths">Attempt to expand short paths if true</param>
+ /// <exception cref="ArgumentException">Thrown if the path is an illegal UNC (does not contain a full server/share) or contains illegal characters.</exception>
+ /// <exception cref="PathTooLongException">Thrown if the path or a path segment exceeds the filesystem limits.</exception>
+ /// <exception cref="FileNotFoundException">Thrown if Windows returns ERROR_FILE_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error)</exception>
+ /// <exception cref="DirectoryNotFoundException">Thrown if Windows returns ERROR_PATH_NOT_FOUND. (See Win32Marshal.GetExceptionForWin32Error)</exception>
+ /// <exception cref="UnauthorizedAccessException">Thrown if Windows returns ERROR_ACCESS_DENIED. (See Win32Marshal.GetExceptionForWin32Error)</exception>
+ /// <exception cref="IOException">Thrown if Windows returns an error that doesn't map to the above. (See Win32Marshal.GetExceptionForWin32Error)</exception>
+ /// <returns>Normalized path</returns>
+ internal static string Normalize(string path, bool checkInvalidCharacters, bool expandShortPaths)
+ {
+ // Get the full path
+ StringBuffer fullPath = new StringBuffer(PathInternal.MaxShortPath);
+
+ try
+ {
+ GetFullPathName(path, ref fullPath);
+
+ // Trim whitespace off the end of the string. Win32 normalization trims only U+0020.
+ fullPath.TrimEnd(PathInternal.s_trimEndChars);
+
+ if (fullPath.Length >= PathInternal.MaxLongPath)
+ {
+ // Fullpath is genuinely too long
+ throw new PathTooLongException(SR.IO_PathTooLong);
+ }
+
+ // Checking path validity used to happen before getting the full path name. To avoid additional input allocation
+ // (to trim trailing whitespace) we now do it after the Win32 call. This will allow legitimate paths through that
+ // used to get kicked back (notably segments with invalid characters might get removed via "..").
+ //
+ // There is no way that GetLongPath can invalidate the path so we'll do this (cheaper) check before we attempt to
+ // expand short file names.
+
+ // Scan the path for:
+ //
+ // - Illegal path characters.
+ // - Invalid UNC paths like \\, \\server, \\server\.
+ // - Segments that are too long (over MaxComponentLength)
+
+ // As the path could be > 30K, we'll combine the validity scan. None of these checks are performed by the Win32
+ // GetFullPathName() API.
+
+ bool possibleShortPath = false;
+ bool foundTilde = false;
+
+ // We can get UNCs as device paths through this code (e.g. \\.\UNC\), we won't validate them as there isn't
+ // an easy way to normalize without extensive cost (we'd have to hunt down the canonical name for any device
+ // path that contains UNC or to see if the path was doing something like \\.\GLOBALROOT\Device\Mup\,
+ // \\.\GLOBAL\UNC\, \\.\GLOBALROOT\GLOBAL??\UNC\, etc.
+ bool specialPath = fullPath.Length > 1 && fullPath[0] == '\\' && fullPath[1] == '\\';
+ bool isDevice = PathInternal.IsDevice(ref fullPath);
+ bool possibleBadUnc = specialPath && !isDevice;
+ int index = specialPath ? 2 : 0;
+ int lastSeparator = specialPath ? 1 : 0;
+ int segmentLength;
+ char current;
+
+ while (index < fullPath.Length)
+ {
+ current = fullPath[index];
+
+ // Try to skip deeper analysis. '?' and higher are valid/ignorable except for '\', '|', and '~'
+ if (current < '?' || current == '\\' || current == '|' || current == '~')
+ {
+ switch (current)
+ {
+ case '|':
+ case '>':
+ case '<':
+ case '\"':
+ if (checkInvalidCharacters) throw new ArgumentException(SR.Argument_InvalidPathChars);
+ foundTilde = false;
+ break;
+ case '~':
+ foundTilde = true;
+ break;
+ case '\\':
+ segmentLength = index - lastSeparator - 1;
+ if (segmentLength > PathInternal.MaxComponentLength)
+ throw new PathTooLongException(SR.IO_PathTooLong + fullPath.ToString());
+ lastSeparator = index;
+
+ if (foundTilde)
+ {
+ if (segmentLength <= MaxShortName)
+ {
+ // Possibly a short path.
+ possibleShortPath = true;
+ }
+
+ foundTilde = false;
+ }
+
+ if (possibleBadUnc)
+ {
+ // If we're at the end of the path and this is the first separator, we're missing the share.
+ // Otherwise we're good, so ignore UNC tracking from here.
+ if (index == fullPath.Length - 1)
+ throw new ArgumentException(SR.Arg_PathIllegalUNC);
+ else
+ possibleBadUnc = false;
+ }
+
+ break;
+
+ default:
+ if (checkInvalidCharacters && current < ' ') throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
+ break;
+ }
+ }
+
+ index++;
+ }
+
+ if (possibleBadUnc)
+ throw new ArgumentException(SR.Arg_PathIllegalUNC);
+
+ segmentLength = fullPath.Length - lastSeparator - 1;
+ if (segmentLength > PathInternal.MaxComponentLength)
+ throw new PathTooLongException(SR.IO_PathTooLong);
+
+ if (foundTilde && segmentLength <= MaxShortName)
+ possibleShortPath = true;
+
+ // Check for a short filename path and try and expand it. Technically you don't need to have a tilde for a short name, but
+ // this is how we've always done this. This expansion is costly so we'll continue to let other short paths slide.
+ if (expandShortPaths && possibleShortPath)
+ {
+ return TryExpandShortFileName(ref fullPath, originalPath: path);
+ }
+ else
+ {
+ if (fullPath.Length == path.Length && fullPath.StartsWith(path))
+ {
+ // If we have the exact same string we were passed in, don't bother to allocate another string from the StringBuilder.
+ return path;
+ }
+ else
+ {
+ return fullPath.ToString();
+ }
+ }
+ }
+ finally
+ {
+ // Clear the buffer
+ fullPath.Free();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool IsDosUnc(ref StringBuffer buffer)
+ {
+ return !PathInternal.IsDevice(ref buffer) && buffer.Length > 1 && buffer[0] == '\\' && buffer[1] == '\\';
+ }
+
+ private static unsafe void GetFullPathName(string path, ref StringBuffer fullPath)
+ {
+ // If the string starts with an extended prefix we would need to remove it from the path before we call GetFullPathName as
+ // it doesn't root extended paths correctly. We don't currently resolve extended paths, so we'll just assert here.
+ Debug.Assert(PathInternal.IsPartiallyQualified(path) || !PathInternal.IsExtended(path));
+
+ // Historically we would skip leading spaces *only* if the path started with a drive " C:" or a UNC " \\"
+ int startIndex = PathInternal.PathStartSkip(path);
+
+ fixed (char* pathStart = path)
+ {
+ uint result = 0;
+ while ((result = Interop.Kernel32.GetFullPathNameW(pathStart + startIndex, (uint)fullPath.Capacity, fullPath.UnderlyingArray, IntPtr.Zero)) > fullPath.Capacity)
+ {
+ // Reported size is greater than the buffer size. Increase the capacity.
+ fullPath.EnsureCapacity(checked((int)result));
+ }
+
+ if (result == 0)
+ {
+ // Failure, get the error and throw
+ int errorCode = Marshal.GetLastWin32Error();
+ if (errorCode == 0)
+ errorCode = Interop.Errors.ERROR_BAD_PATHNAME;
+ throw Win32Marshal.GetExceptionForWin32Error(errorCode, path);
+ }
+
+ fullPath.Length = checked((int)result);
+ }
+ }
+
+ private static int GetInputBuffer(ref StringBuffer content, bool isDosUnc, ref StringBuffer buffer)
+ {
+ int length = content.Length;
+
+ length += isDosUnc
+ ? PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength
+ : PathInternal.DevicePrefixLength;
+
+ buffer.EnsureCapacity(length + 1);
+
+ if (isDosUnc)
+ {
+ // Put the extended UNC prefix (\\?\UNC\) in front of the path
+ buffer.CopyFrom(bufferIndex: 0, source: PathInternal.UncExtendedPathPrefix);
+
+ // Copy the source buffer over after the existing UNC prefix
+ content.CopyTo(
+ bufferIndex: PathInternal.UncPrefixLength,
+ destination: ref buffer,
+ destinationIndex: PathInternal.UncExtendedPrefixLength,
+ count: content.Length - PathInternal.UncPrefixLength);
+
+ // Return the prefix difference
+ return PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength;
+ }
+ else
+ {
+ int prefixSize = PathInternal.ExtendedPathPrefix.Length;
+ buffer.CopyFrom(bufferIndex: 0, source: PathInternal.ExtendedPathPrefix);
+ content.CopyTo(bufferIndex: 0, destination: ref buffer, destinationIndex: prefixSize, count: content.Length);
+ return prefixSize;
+ }
+ }
+
+ private static string TryExpandShortFileName(ref StringBuffer outputBuffer, string originalPath)
+ {
+ // We guarantee we'll expand short names for paths that only partially exist. As such, we need to find the part of the path that actually does exist. To
+ // avoid allocating like crazy we'll create only one input array and modify the contents with embedded nulls.
+
+ Debug.Assert(!PathInternal.IsPartiallyQualified(ref outputBuffer), "should have resolved by now");
+
+ // We'll have one of a few cases by now (the normalized path will have already:
+ //
+ // 1. Dos path (C:\)
+ // 2. Dos UNC (\\Server\Share)
+ // 3. Dos device path (\\.\C:\, \\?\C:\)
+ //
+ // We want to put the extended syntax on the front if it doesn't already have it, which may mean switching from \\.\.
+ //
+ // Note that we will never get \??\ here as GetFullPathName() does not recognize \??\ and will return it as C:\??\ (or whatever the current drive is).
+
+ int rootLength = PathInternal.GetRootLength(ref outputBuffer);
+ bool isDevice = PathInternal.IsDevice(ref outputBuffer);
+
+ StringBuffer inputBuffer = new StringBuffer(0);
+ try
+ {
+ bool isDosUnc = false;
+ int rootDifference = 0;
+ bool wasDotDevice = false;
+
+ // Add the extended prefix before expanding to allow growth over MAX_PATH
+ if (isDevice)
+ {
+ // We have one of the following (\\?\ or \\.\)
+ inputBuffer.Append(ref outputBuffer);
+
+ if (outputBuffer[2] == '.')
+ {
+ wasDotDevice = true;
+ inputBuffer[2] = '?';
+ }
+ }
+ else
+ {
+ isDosUnc = IsDosUnc(ref outputBuffer);
+ rootDifference = GetInputBuffer(ref outputBuffer, isDosUnc, ref inputBuffer);
+ }
+
+ rootLength += rootDifference;
+ int inputLength = inputBuffer.Length;
+
+ bool success = false;
+ int foundIndex = inputBuffer.Length - 1;
+
+ while (!success)
+ {
+ uint result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity);
+
+ // Replace any temporary null we added
+ if (inputBuffer[foundIndex] == '\0') inputBuffer[foundIndex] = '\\';
+
+ if (result == 0)
+ {
+ // Look to see if we couldn't find the file
+ int error = Marshal.GetLastWin32Error();
+ if (error != Interop.Errors.ERROR_FILE_NOT_FOUND && error != Interop.Errors.ERROR_PATH_NOT_FOUND)
+ {
+ // Some other failure, give up
+ break;
+ }
+
+ // We couldn't find the path at the given index, start looking further back in the string.
+ foundIndex--;
+
+ for (; foundIndex > rootLength && inputBuffer[foundIndex] != '\\'; foundIndex--) ;
+ if (foundIndex == rootLength)
+ {
+ // Can't trim the path back any further
+ break;
+ }
+ else
+ {
+ // Temporarily set a null in the string to get Windows to look further up the path
+ inputBuffer[foundIndex] = '\0';
+ }
+ }
+ else if (result > outputBuffer.Capacity)
+ {
+ // Not enough space. The result count for this API does not include the null terminator.
+ outputBuffer.EnsureCapacity(checked((int)result));
+ result = Interop.Kernel32.GetLongPathNameW(inputBuffer.UnderlyingArray, outputBuffer.UnderlyingArray, (uint)outputBuffer.Capacity);
+ }
+ else
+ {
+ // Found the path
+ success = true;
+ outputBuffer.Length = checked((int)result);
+ if (foundIndex < inputLength - 1)
+ {
+ // It was a partial find, put the non-existent part of the path back
+ outputBuffer.Append(ref inputBuffer, foundIndex, inputBuffer.Length - foundIndex);
+ }
+ }
+ }
+
+ // Strip out the prefix and return the string
+ ref StringBuffer bufferToUse = ref Choose(success, ref outputBuffer, ref inputBuffer);
+
+ // Switch back from \\?\ to \\.\ if necessary
+ if (wasDotDevice)
+ bufferToUse[2] = '.';
+
+ string returnValue = null;
+
+ int newLength = (int)(bufferToUse.Length - rootDifference);
+ if (isDosUnc)
+ {
+ // Need to go from \\?\UNC\ to \\?\UN\\
+ bufferToUse[PathInternal.UncExtendedPrefixLength - PathInternal.UncPrefixLength] = '\\';
+ }
+
+ // We now need to strip out any added characters at the front of the string
+ if (bufferToUse.SubstringEquals(originalPath, rootDifference, newLength))
+ {
+ // Use the original path to avoid allocating
+ returnValue = originalPath;
+ }
+ else
+ {
+ returnValue = bufferToUse.Substring(rootDifference, newLength);
+ }
+
+ return returnValue;
+ }
+ finally
+ {
+ inputBuffer.Free();
+ }
+ }
+
+ // Helper method to workaround lack of operator ? support for ref values
+ private static ref StringBuffer Choose(bool condition, ref StringBuffer s1, ref StringBuffer s2)
+ {
+ if (condition) return ref s1;
+ else return ref s2;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/PathInternal.Unix.cs b/src/mscorlib/shared/System/IO/PathInternal.Unix.cs
new file mode 100644
index 0000000000..08dc1d0251
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/PathInternal.Unix.cs
@@ -0,0 +1,104 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Text;
+
+namespace System.IO
+{
+ /// <summary>Contains internal path helpers that are shared between many projects.</summary>
+ internal static partial class PathInternal
+ {
+ internal const char DirectorySeparatorChar = '/';
+ internal const char AltDirectorySeparatorChar = '/';
+ internal const char VolumeSeparatorChar = '/';
+ internal const char PathSeparator = ':';
+
+ internal const string DirectorySeparatorCharAsString = "/";
+
+ // There is only one invalid path character in Unix
+ private const char InvalidPathChar = '\0';
+
+ internal const string ParentDirectoryPrefix = @"../";
+
+ /// <summary>Returns a value indicating if the given path contains invalid characters.</summary>
+ internal static bool HasIllegalCharacters(string path)
+ {
+ Debug.Assert(path != null);
+ return path.IndexOf(InvalidPathChar) >= 0;
+ }
+
+ internal static int GetRootLength(string path)
+ {
+ return path.Length > 0 && IsDirectorySeparator(path[0]) ? 1 : 0;
+ }
+
+ internal static bool IsDirectorySeparator(char c)
+ {
+ // The alternate directory separator char is the same as the directory separator,
+ // so we only need to check one.
+ Debug.Assert(DirectorySeparatorChar == AltDirectorySeparatorChar);
+ return c == DirectorySeparatorChar;
+ }
+
+ /// <summary>
+ /// Normalize separators in the given path. Compresses forward slash runs.
+ /// </summary>
+ internal static string NormalizeDirectorySeparators(string path)
+ {
+ if (string.IsNullOrEmpty(path)) return path;
+
+ // Make a pass to see if we need to normalize so we can potentially skip allocating
+ bool normalized = true;
+
+ for (int i = 0; i < path.Length; i++)
+ {
+ if (IsDirectorySeparator(path[i])
+ && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])))
+ {
+ normalized = false;
+ break;
+ }
+ }
+
+ if (normalized) return path;
+
+ StringBuilder builder = new StringBuilder(path.Length);
+
+ for (int i = 0; i < path.Length; i++)
+ {
+ char current = path[i];
+
+ // Skip if we have another separator following
+ if (IsDirectorySeparator(current)
+ && (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])))
+ continue;
+
+ builder.Append(current);
+ }
+
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Returns true if the character is a directory or volume separator.
+ /// </summary>
+ /// <param name="ch">The character to test.</param>
+ internal static bool IsDirectoryOrVolumeSeparator(char ch)
+ {
+ // The directory separator, volume separator, and the alternate directory
+ // separator should be the same on Unix, so we only need to check one.
+ Debug.Assert(DirectorySeparatorChar == AltDirectorySeparatorChar);
+ Debug.Assert(DirectorySeparatorChar == VolumeSeparatorChar);
+ return ch == DirectorySeparatorChar;
+ }
+
+ internal static bool IsPartiallyQualified(string path)
+ {
+ // This is much simpler than Windows where paths can be rooted, but not fully qualified (such as Drive Relative)
+ // As long as the path is rooted in Unix it doesn't use the current directory and therefore is fully qualified.
+ return !Path.IsPathRooted(path);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/PathInternal.Windows.StringBuffer.cs b/src/mscorlib/shared/System/IO/PathInternal.Windows.StringBuffer.cs
new file mode 100644
index 0000000000..84953df37b
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/PathInternal.Windows.StringBuffer.cs
@@ -0,0 +1,93 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace System.IO
+{
+ /// <summary>Contains internal path helpers that are shared between many projects.</summary>
+ internal static partial class PathInternal
+ {
+ /// <summary>
+ /// Returns true if the path uses the extended syntax (\\?\)
+ /// </summary>
+ internal static bool IsExtended(ref StringBuffer path)
+ {
+ // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths.
+ // Skipping of normalization will *only* occur if back slashes ('\') are used.
+ return path.Length >= DevicePrefixLength
+ && path[0] == '\\'
+ && (path[1] == '\\' || path[1] == '?')
+ && path[2] == '?'
+ && path[3] == '\\';
+ }
+
+ /// <summary>
+ /// Gets the length of the root of the path (drive, share, etc.).
+ /// </summary>
+ internal unsafe static int GetRootLength(ref StringBuffer path)
+ {
+ if (path.Length == 0) return 0;
+
+ fixed (char* value = path.UnderlyingArray)
+ {
+ return GetRootLength(value, path.Length);
+ }
+ }
+
+ /// <summary>
+ /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\")
+ /// </summary>
+ internal static bool IsDevice(ref StringBuffer path)
+ {
+ // If the path begins with any two separators is will be recognized and normalized and prepped with
+ // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not.
+ return IsExtended(ref path)
+ ||
+ (
+ path.Length >= DevicePrefixLength
+ && IsDirectorySeparator(path[0])
+ && IsDirectorySeparator(path[1])
+ && (path[2] == '.' || path[2] == '?')
+ && IsDirectorySeparator(path[3])
+ );
+ }
+
+ /// <summary>
+ /// Returns true if the path specified is relative to the current drive or working directory.
+ /// Returns false if the path is fixed to a specific drive or UNC path. This method does no
+ /// validation of the path (URIs will be returned as relative as a result).
+ /// </summary>
+ /// <remarks>
+ /// Handles paths that use the alternate directory separator. It is a frequent mistake to
+ /// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
+ /// "C:a" is drive relative- meaning that it will be resolved against the current directory
+ /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
+ /// will not be used to modify the path).
+ /// </remarks>
+ internal static bool IsPartiallyQualified(ref StringBuffer path)
+ {
+ if (path.Length < 2)
+ {
+ // It isn't fixed, it must be relative. There is no way to specify a fixed
+ // path with one character (or less).
+ return true;
+ }
+
+ if (IsDirectorySeparator(path[0]))
+ {
+ // There is no valid way to specify a relative path with two initial slashes or
+ // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\
+ return !(path[1] == '?' || IsDirectorySeparator(path[1]));
+ }
+
+ // The only way to specify a fixed path that doesn't begin with two slashes
+ // is the drive, colon, slash format- i.e. C:\
+ return !((path.Length >= 3)
+ && (path[1] == VolumeSeparatorChar)
+ && IsDirectorySeparator(path[2]));
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/PathInternal.Windows.cs b/src/mscorlib/shared/System/IO/PathInternal.Windows.cs
new file mode 100644
index 0000000000..ee0dd54383
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/PathInternal.Windows.cs
@@ -0,0 +1,442 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace System.IO
+{
+ /// <summary>Contains internal path helpers that are shared between many projects.</summary>
+ internal static partial class PathInternal
+ {
+ // All paths in Win32 ultimately end up becoming a path to a File object in the Windows object manager. Passed in paths get mapped through
+ // DosDevice symbolic links in the object tree to actual File objects under \Devices. To illustrate, this is what happens with a typical
+ // path "Foo" passed as a filename to any Win32 API:
+ //
+ // 1. "Foo" is recognized as a relative path and is appended to the current directory (say, "C:\" in our example)
+ // 2. "C:\Foo" is prepended with the DosDevice namespace "\??\"
+ // 3. CreateFile tries to create an object handle to the requested file "\??\C:\Foo"
+ // 4. The Object Manager recognizes the DosDevices prefix and looks
+ // a. First in the current session DosDevices ("\Sessions\1\DosDevices\" for example, mapped network drives go here)
+ // b. If not found in the session, it looks in the Global DosDevices ("\GLOBAL??\")
+ // 5. "C:" is found in DosDevices (in our case "\GLOBAL??\C:", which is a symbolic link to "\Device\HarddiskVolume6")
+ // 6. The full path is now "\Device\HarddiskVolume6\Foo", "\Device\HarddiskVolume6" is a File object and parsing is handed off
+ // to the registered parsing method for Files
+ // 7. The registered open method for File objects is invoked to create the file handle which is then returned
+ //
+ // There are multiple ways to directly specify a DosDevices path. The final format of "\??\" is one way. It can also be specified
+ // as "\\.\" (the most commonly documented way) and "\\?\". If the question mark syntax is used the path will skip normalization
+ // (essentially GetFullPathName()) and path length checks.
+
+ // Windows Kernel-Mode Object Manager
+ // https://msdn.microsoft.com/en-us/library/windows/hardware/ff565763.aspx
+ // https://channel9.msdn.com/Shows/Going+Deep/Windows-NT-Object-Manager
+ //
+ // Introduction to MS-DOS Device Names
+ // https://msdn.microsoft.com/en-us/library/windows/hardware/ff548088.aspx
+ //
+ // Local and Global MS-DOS Device Names
+ // https://msdn.microsoft.com/en-us/library/windows/hardware/ff554302.aspx
+
+ internal const char DirectorySeparatorChar = '\\';
+ internal const char AltDirectorySeparatorChar = '/';
+ internal const char VolumeSeparatorChar = ':';
+ internal const char PathSeparator = ';';
+
+ internal const string DirectorySeparatorCharAsString = "\\";
+
+ internal const string ExtendedPathPrefix = @"\\?\";
+ internal const string UncPathPrefix = @"\\";
+ internal const string UncExtendedPrefixToInsert = @"?\UNC\";
+ internal const string UncExtendedPathPrefix = @"\\?\UNC\";
+ internal const string DevicePathPrefix = @"\\.\";
+ internal const string ParentDirectoryPrefix = @"..\";
+
+ internal const int MaxShortPath = 260;
+ internal const int MaxShortDirectoryPath = 248;
+ internal const int MaxLongPath = short.MaxValue;
+ // \\?\, \\.\, \??\
+ internal const int DevicePrefixLength = 4;
+ // \\
+ internal const int UncPrefixLength = 2;
+ // \\?\UNC\, \\.\UNC\
+ internal const int UncExtendedPrefixLength = 8;
+ internal const int MaxComponentLength = 255;
+
+ /// <summary>
+ /// Returns true if the given character is a valid drive letter
+ /// </summary>
+ internal static bool IsValidDriveChar(char value)
+ {
+ return ((value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z'));
+ }
+
+ /// <summary>
+ /// Adds the extended path prefix (\\?\) if not already a device path, IF the path is not relative,
+ /// AND the path is more than 259 characters. (> MAX_PATH + null)
+ /// </summary>
+ internal static string EnsureExtendedPrefixOverMaxPath(string path)
+ {
+ if (path != null && path.Length >= MaxShortPath)
+ {
+ return EnsureExtendedPrefix(path);
+ }
+ else
+ {
+ return path;
+ }
+ }
+
+ /// <summary>
+ /// Adds the extended path prefix (\\?\) if not relative or already a device path.
+ /// </summary>
+ internal static string EnsureExtendedPrefix(string path)
+ {
+ // Putting the extended prefix on the path changes the processing of the path. It won't get normalized, which
+ // means adding to relative paths will prevent them from getting the appropriate current directory inserted.
+
+ // If it already has some variant of a device path (\??\, \\?\, \\.\, //./, etc.) we don't need to change it
+ // as it is either correct or we will be changing the behavior. When/if Windows supports long paths implicitly
+ // in the future we wouldn't want normalization to come back and break existing code.
+
+ // In any case, all internal usages should be hitting normalize path (Path.GetFullPath) before they hit this
+ // shimming method. (Or making a change that doesn't impact normalization, such as adding a filename to a
+ // normalized base path.)
+ if (IsPartiallyQualified(path) || IsDevice(path))
+ return path;
+
+ // Given \\server\share in longpath becomes \\?\UNC\server\share
+ if (path.StartsWith(UncPathPrefix, StringComparison.OrdinalIgnoreCase))
+ return path.Insert(2, UncExtendedPrefixToInsert);
+
+ return ExtendedPathPrefix + path;
+ }
+
+ /// <summary>
+ /// Returns true if the path uses any of the DOS device path syntaxes. ("\\.\", "\\?\", or "\??\")
+ /// </summary>
+ internal static bool IsDevice(string path)
+ {
+ // If the path begins with any two separators is will be recognized and normalized and prepped with
+ // "\??\" for internal usage correctly. "\??\" is recognized and handled, "/??/" is not.
+ return IsExtended(path)
+ ||
+ (
+ path.Length >= DevicePrefixLength
+ && IsDirectorySeparator(path[0])
+ && IsDirectorySeparator(path[1])
+ && (path[2] == '.' || path[2] == '?')
+ && IsDirectorySeparator(path[3])
+ );
+ }
+
+ /// <summary>
+ /// Returns true if the path uses the canonical form of extended syntax ("\\?\" or "\??\"). If the
+ /// path matches exactly (cannot use alternate directory separators) Windows will skip normalization
+ /// and path length checks.
+ /// </summary>
+ internal static bool IsExtended(string path)
+ {
+ // While paths like "//?/C:/" will work, they're treated the same as "\\.\" paths.
+ // Skipping of normalization will *only* occur if back slashes ('\') are used.
+ return path.Length >= DevicePrefixLength
+ && path[0] == '\\'
+ && (path[1] == '\\' || path[1] == '?')
+ && path[2] == '?'
+ && path[3] == '\\';
+ }
+
+ /// <summary>
+ /// Returns a value indicating if the given path contains invalid characters (", &lt;, &gt;, |
+ /// NUL, or any ASCII char whose integer representation is in the range of 1 through 31).
+ /// Does not check for wild card characters ? and *.
+ /// </summary>
+ internal static bool HasIllegalCharacters(string path)
+ {
+ // This is equivalent to IndexOfAny(InvalidPathChars) >= 0,
+ // except faster since IndexOfAny grows slower as the input
+ // array grows larger.
+ // Since we know that some of the characters we're looking
+ // for are contiguous in the alphabet-- the path cannot contain
+ // characters 0-31-- we can optimize this for our specific use
+ // case and use simple comparison operations.
+
+ for (int i = 0; i < path.Length; i++)
+ {
+ char c = path[i];
+ if (c <= '|') // fast path for common case - '|' is highest illegal character
+ {
+ if (c <= '\u001f' || c == '|')
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Check for known wildcard characters. '*' and '?' are the most common ones.
+ /// </summary>
+ internal static bool HasWildCardCharacters(string path)
+ {
+ // Question mark is part of dos device syntax so we have to skip if we are
+ int startIndex = IsDevice(path) ? ExtendedPathPrefix.Length : 0;
+
+ // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression
+ // https://msdn.microsoft.com/en-us/library/ff469270.aspx
+ for (int i = startIndex; i < path.Length; i++)
+ {
+ char c = path[i];
+ if (c <= '?') // fast path for common case - '?' is highest wildcard character
+ {
+ if (c == '\"' || c == '<' || c == '>' || c == '*' || c == '?')
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Gets the length of the root of the path (drive, share, etc.).
+ /// </summary>
+ internal unsafe static int GetRootLength(string path)
+ {
+ fixed (char* value = path)
+ {
+ return GetRootLength(value, path.Length);
+ }
+ }
+
+ private unsafe static int GetRootLength(char* path, int pathLength)
+ {
+ int i = 0;
+ int volumeSeparatorLength = 2; // Length to the colon "C:"
+ int uncRootLength = 2; // Length to the start of the server name "\\"
+
+ bool extendedSyntax = StartsWithOrdinal(path, pathLength, ExtendedPathPrefix);
+ bool extendedUncSyntax = StartsWithOrdinal(path, pathLength, UncExtendedPathPrefix);
+ if (extendedSyntax)
+ {
+ // Shift the position we look for the root from to account for the extended prefix
+ if (extendedUncSyntax)
+ {
+ // "\\" -> "\\?\UNC\"
+ uncRootLength = UncExtendedPathPrefix.Length;
+ }
+ else
+ {
+ // "C:" -> "\\?\C:"
+ volumeSeparatorLength += ExtendedPathPrefix.Length;
+ }
+ }
+
+ if ((!extendedSyntax || extendedUncSyntax) && pathLength > 0 && IsDirectorySeparator(path[0]))
+ {
+ // UNC or simple rooted path (e.g. "\foo", NOT "\\?\C:\foo")
+
+ i = 1; // Drive rooted (\foo) is one character
+ if (extendedUncSyntax || (pathLength > 1 && IsDirectorySeparator(path[1])))
+ {
+ // UNC (\\?\UNC\ or \\), scan past the next two directory separators at most
+ // (e.g. to \\?\UNC\Server\Share or \\Server\Share\)
+ i = uncRootLength;
+ int n = 2; // Maximum separators to skip
+ while (i < pathLength && (!IsDirectorySeparator(path[i]) || --n > 0)) i++;
+ }
+ }
+ else if (pathLength >= volumeSeparatorLength && path[volumeSeparatorLength - 1] == VolumeSeparatorChar)
+ {
+ // Path is at least longer than where we expect a colon, and has a colon (\\?\A:, A:)
+ // If the colon is followed by a directory separator, move past it
+ i = volumeSeparatorLength;
+ if (pathLength >= volumeSeparatorLength + 1 && IsDirectorySeparator(path[volumeSeparatorLength])) i++;
+ }
+ return i;
+ }
+
+ private unsafe static bool StartsWithOrdinal(char* source, int sourceLength, string value)
+ {
+ if (sourceLength < value.Length) return false;
+ for (int i = 0; i < value.Length; i++)
+ {
+ if (value[i] != source[i]) return false;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Returns true if the path specified is relative to the current drive or working directory.
+ /// Returns false if the path is fixed to a specific drive or UNC path. This method does no
+ /// validation of the path (URIs will be returned as relative as a result).
+ /// </summary>
+ /// <remarks>
+ /// Handles paths that use the alternate directory separator. It is a frequent mistake to
+ /// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
+ /// "C:a" is drive relative- meaning that it will be resolved against the current directory
+ /// for C: (rooted, but relative). "C:\a" is rooted and not relative (the current directory
+ /// will not be used to modify the path).
+ /// </remarks>
+ internal static bool IsPartiallyQualified(string path)
+ {
+ if (path.Length < 2)
+ {
+ // It isn't fixed, it must be relative. There is no way to specify a fixed
+ // path with one character (or less).
+ return true;
+ }
+
+ if (IsDirectorySeparator(path[0]))
+ {
+ // There is no valid way to specify a relative path with two initial slashes or
+ // \? as ? isn't valid for drive relative paths and \??\ is equivalent to \\?\
+ return !(path[1] == '?' || IsDirectorySeparator(path[1]));
+ }
+
+ // The only way to specify a fixed path that doesn't begin with two slashes
+ // is the drive, colon, slash format- i.e. C:\
+ return !((path.Length >= 3)
+ && (path[1] == VolumeSeparatorChar)
+ && IsDirectorySeparator(path[2])
+ // To match old behavior we'll check the drive character for validity as the path is technically
+ // not qualified if you don't have a valid drive. "=:\" is the "=" file's default data stream.
+ && IsValidDriveChar(path[0]));
+ }
+
+ /// <summary>
+ /// Returns the characters to skip at the start of the path if it starts with space(s) and a drive or directory separator.
+ /// (examples are " C:", " \")
+ /// This is a legacy behavior of Path.GetFullPath().
+ /// </summary>
+ /// <remarks>
+ /// Note that this conflicts with IsPathRooted() which doesn't (and never did) such a skip.
+ /// </remarks>
+ internal static int PathStartSkip(string path)
+ {
+ int startIndex = 0;
+ while (startIndex < path.Length && path[startIndex] == ' ') startIndex++;
+
+ if (startIndex > 0 && (startIndex < path.Length && IsDirectorySeparator(path[startIndex]))
+ || (startIndex + 1 < path.Length && path[startIndex + 1] == ':' && IsValidDriveChar(path[startIndex])))
+ {
+ // Go ahead and skip spaces as we're either " C:" or " \"
+ return startIndex;
+ }
+
+ return 0;
+ }
+
+ /// <summary>
+ /// True if the given character is a directory separator.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool IsDirectorySeparator(char c)
+ {
+ return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
+ }
+
+ /// <summary>
+ /// Normalize separators in the given path. Converts forward slashes into back slashes and compresses slash runs, keeping initial 2 if present.
+ /// Also trims initial whitespace in front of "rooted" paths (see PathStartSkip).
+ ///
+ /// This effectively replicates the behavior of the legacy NormalizePath when it was called with fullCheck=false and expandShortpaths=false.
+ /// The current NormalizePath gets directory separator normalization from Win32's GetFullPathName(), which will resolve relative paths and as
+ /// such can't be used here (and is overkill for our uses).
+ ///
+ /// Like the current NormalizePath this will not try and analyze periods/spaces within directory segments.
+ /// </summary>
+ /// <remarks>
+ /// The only callers that used to use Path.Normalize(fullCheck=false) were Path.GetDirectoryName() and Path.GetPathRoot(). Both usages do
+ /// not need trimming of trailing whitespace here.
+ ///
+ /// GetPathRoot() could technically skip normalizing separators after the second segment- consider as a future optimization.
+ ///
+ /// For legacy desktop behavior with ExpandShortPaths:
+ /// - It has no impact on GetPathRoot() so doesn't need consideration.
+ /// - It could impact GetDirectoryName(), but only if the path isn't relative (C:\ or \\Server\Share).
+ ///
+ /// In the case of GetDirectoryName() the ExpandShortPaths behavior was undocumented and provided inconsistent results if the path was
+ /// fixed/relative. For example: "C:\PROGRA~1\A.TXT" would return "C:\Program Files" while ".\PROGRA~1\A.TXT" would return ".\PROGRA~1". If you
+ /// ultimately call GetFullPath() this doesn't matter, but if you don't or have any intermediate string handling could easily be tripped up by
+ /// this undocumented behavior.
+ ///
+ /// We won't match this old behavior because:
+ ///
+ /// 1. It was undocumented
+ /// 2. It was costly (extremely so if it actually contained '~')
+ /// 3. Doesn't play nice with string logic
+ /// 4. Isn't a cross-plat friendly concept/behavior
+ /// </remarks>
+ internal static string NormalizeDirectorySeparators(string path)
+ {
+ if (string.IsNullOrEmpty(path)) return path;
+
+ char current;
+ int start = PathStartSkip(path);
+
+ if (start == 0)
+ {
+ // Make a pass to see if we need to normalize so we can potentially skip allocating
+ bool normalized = true;
+
+ for (int i = 0; i < path.Length; i++)
+ {
+ current = path[i];
+ if (IsDirectorySeparator(current)
+ && (current != DirectorySeparatorChar
+ // Check for sequential separators past the first position (we need to keep initial two for UNC/extended)
+ || (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))))
+ {
+ normalized = false;
+ break;
+ }
+ }
+
+ if (normalized) return path;
+ }
+
+ StringBuilder builder = new StringBuilder(path.Length);
+
+ if (IsDirectorySeparator(path[start]))
+ {
+ start++;
+ builder.Append(DirectorySeparatorChar);
+ }
+
+ for (int i = start; i < path.Length; i++)
+ {
+ current = path[i];
+
+ // If we have a separator
+ if (IsDirectorySeparator(current))
+ {
+ // If the next is a separator, skip adding this
+ if (i + 1 < path.Length && IsDirectorySeparator(path[i + 1]))
+ {
+ continue;
+ }
+
+ // Ensure it is the primary separator
+ current = DirectorySeparatorChar;
+ }
+
+ builder.Append(current);
+ }
+
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Returns true if the character is a directory or volume separator.
+ /// </summary>
+ /// <param name="ch">The character to test.</param>
+ internal static bool IsDirectoryOrVolumeSeparator(char ch)
+ {
+ return IsDirectorySeparator(ch) || VolumeSeparatorChar == ch;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/PathInternal.cs b/src/mscorlib/shared/System/IO/PathInternal.cs
new file mode 100644
index 0000000000..0dab5b968a
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/PathInternal.cs
@@ -0,0 +1,171 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Text;
+
+namespace System.IO
+{
+ /// <summary>Contains internal path helpers that are shared between many projects.</summary>
+ internal static partial class PathInternal
+ {
+ // Trim trailing white spaces, tabs etc but don't be aggressive in removing everything that has UnicodeCategory of trailing space.
+ // string.WhitespaceChars will trim more aggressively than what the underlying FS does (for ex, NTFS, FAT).
+ //
+ // (This is for compatibility with old behavior.)
+ internal static readonly char[] s_trimEndChars =
+ {
+ (char)0x9, // Horizontal tab
+ (char)0xA, // Line feed
+ (char)0xB, // Vertical tab
+ (char)0xC, // Form feed
+ (char)0xD, // Carriage return
+ (char)0x20, // Space
+ (char)0x85, // Next line
+ (char)0xA0 // Non breaking space
+ };
+
+ /// <summary>
+ /// Checks for invalid path characters in the given path.
+ /// </summary>
+ /// <exception cref="System.ArgumentNullException">Thrown if the path is null.</exception>
+ /// <exception cref="System.ArgumentException">Thrown if the path has invalid characters.</exception>
+ /// <param name="path">The path to check for invalid characters.</param>
+ internal static void CheckInvalidPathChars(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+
+ if (HasIllegalCharacters(path))
+ throw new ArgumentException(SR.Argument_InvalidPathChars, nameof(path));
+ }
+
+ /// <summary>
+ /// Returns the start index of the filename
+ /// in the given path, or 0 if no directory
+ /// or volume separator is found.
+ /// </summary>
+ /// <param name="path">The path in which to find the index of the filename.</param>
+ /// <remarks>
+ /// This method returns path.Length for
+ /// inputs like "/usr/foo/" on Unix. As such,
+ /// it is not safe for being used to index
+ /// the string without additional verification.
+ /// </remarks>
+ internal static int FindFileNameIndex(string path)
+ {
+ Debug.Assert(path != null);
+ CheckInvalidPathChars(path);
+
+ for (int i = path.Length - 1; i >= 0; i--)
+ {
+ char ch = path[i];
+ if (IsDirectoryOrVolumeSeparator(ch))
+ return i + 1;
+ }
+
+ return 0; // the whole path is the filename
+ }
+
+ /// <summary>
+ /// Returns true if the path ends in a directory separator.
+ /// </summary>
+ internal static bool EndsInDirectorySeparator(string path) =>
+ !string.IsNullOrEmpty(path) && IsDirectorySeparator(path[path.Length - 1]);
+
+ /// <summary>
+ /// Get the common path length from the start of the string.
+ /// </summary>
+ internal static int GetCommonPathLength(string first, string second, bool ignoreCase)
+ {
+ int commonChars = EqualStartingCharacterCount(first, second, ignoreCase: ignoreCase);
+
+ // If nothing matches
+ if (commonChars == 0)
+ return commonChars;
+
+ // Or we're a full string and equal length or match to a separator
+ if (commonChars == first.Length
+ && (commonChars == second.Length || IsDirectorySeparator(second[commonChars])))
+ return commonChars;
+
+ if (commonChars == second.Length && IsDirectorySeparator(first[commonChars]))
+ return commonChars;
+
+ // It's possible we matched somewhere in the middle of a segment e.g. C:\Foodie and C:\Foobar.
+ while (commonChars > 0 && !IsDirectorySeparator(first[commonChars - 1]))
+ commonChars--;
+
+ return commonChars;
+ }
+
+ /// <summary>
+ /// Gets the count of common characters from the left optionally ignoring case
+ /// </summary>
+ unsafe internal static int EqualStartingCharacterCount(string first, string second, bool ignoreCase)
+ {
+ if (string.IsNullOrEmpty(first) || string.IsNullOrEmpty(second)) return 0;
+
+ int commonChars = 0;
+
+ fixed (char* f = first)
+ fixed (char* s = second)
+ {
+ char* l = f;
+ char* r = s;
+ char* leftEnd = l + first.Length;
+ char* rightEnd = r + second.Length;
+
+ while (l != leftEnd && r != rightEnd
+ && (*l == *r || (ignoreCase && char.ToUpperInvariant((*l)) == char.ToUpperInvariant((*r)))))
+ {
+ commonChars++;
+ l++;
+ r++;
+ }
+ }
+
+ return commonChars;
+ }
+
+ /// <summary>
+ /// Returns true if the two paths have the same root
+ /// </summary>
+ internal static bool AreRootsEqual(string first, string second, StringComparison comparisonType)
+ {
+ int firstRootLength = GetRootLength(first);
+ int secondRootLength = GetRootLength(second);
+
+ return firstRootLength == secondRootLength
+ && string.Compare(
+ strA: first,
+ indexA: 0,
+ strB: second,
+ indexB: 0,
+ length: firstRootLength,
+ comparisonType: comparisonType) == 0;
+ }
+
+ /// <summary>
+ /// Returns false for ".." unless it is specified as a part of a valid File/Directory name.
+ /// (Used to avoid moving up directories.)
+ ///
+ /// Valid: a..b abc..d
+ /// Invalid: ..ab ab.. .. abc..d\abc..
+ /// </summary>
+ internal static void CheckSearchPattern(string searchPattern)
+ {
+ int index;
+ while ((index = searchPattern.IndexOf("..", StringComparison.Ordinal)) != -1)
+ {
+ // Terminal ".." . Files names cannot end in ".."
+ if (index + 2 == searchPattern.Length
+ || IsDirectorySeparator(searchPattern[index + 2]))
+ throw new ArgumentException(SR.Arg_InvalidSearchPattern);
+
+ searchPattern = searchPattern.Substring(index + 2);
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/PathTooLongException.cs b/src/mscorlib/shared/System/IO/PathTooLongException.cs
new file mode 100644
index 0000000000..613af051ca
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/PathTooLongException.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.IO
+{
+ [Serializable]
+ public class PathTooLongException : IOException
+ {
+ public PathTooLongException()
+ : base(SR.IO_PathTooLong)
+ {
+ HResult = __HResults.COR_E_PATHTOOLONG;
+ }
+
+ public PathTooLongException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_PATHTOOLONG;
+ }
+
+ public PathTooLongException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_PATHTOOLONG;
+ }
+
+ protected PathTooLongException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/SeekOrigin.cs b/src/mscorlib/shared/System/IO/SeekOrigin.cs
new file mode 100644
index 0000000000..3798a0ce70
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/SeekOrigin.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.IO
+{
+ // Provides seek reference points. To seek to the end of a stream,
+ // call stream.Seek(0, SeekOrigin.End).
+ public enum SeekOrigin
+ {
+ // These constants match Win32's FILE_BEGIN, FILE_CURRENT, and FILE_END
+ Begin = 0,
+ Current = 1,
+ End = 2,
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/StreamHelpers.CopyValidation.cs b/src/mscorlib/shared/System/IO/StreamHelpers.CopyValidation.cs
new file mode 100644
index 0000000000..45bbd816df
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/StreamHelpers.CopyValidation.cs
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.IO
+{
+ /// <summary>Provides methods to help in the implementation of Stream-derived types.</summary>
+ internal static partial class StreamHelpers
+ {
+ /// <summary>Validate the arguments to CopyTo, as would Stream.CopyTo.</summary>
+ public static void ValidateCopyToArgs(Stream source, Stream destination, int bufferSize)
+ {
+ if (destination == null)
+ {
+ throw new ArgumentNullException(nameof(destination));
+ }
+
+ if (bufferSize <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, SR.ArgumentOutOfRange_NeedPosNum);
+ }
+
+ bool sourceCanRead = source.CanRead;
+ if (!sourceCanRead && !source.CanWrite)
+ {
+ throw new ObjectDisposedException(null, SR.ObjectDisposed_StreamClosed);
+ }
+
+ bool destinationCanWrite = destination.CanWrite;
+ if (!destinationCanWrite && !destination.CanRead)
+ {
+ throw new ObjectDisposedException(nameof(destination), SR.ObjectDisposed_StreamClosed);
+ }
+
+ if (!sourceCanRead)
+ {
+ throw new NotSupportedException(SR.NotSupported_UnreadableStream);
+ }
+
+ if (!destinationCanWrite)
+ {
+ throw new NotSupportedException(SR.NotSupported_UnwritableStream);
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IO/Win32Marshal.cs b/src/mscorlib/shared/System/IO/Win32Marshal.cs
new file mode 100644
index 0000000000..ef76c27010
--- /dev/null
+++ b/src/mscorlib/shared/System/IO/Win32Marshal.cs
@@ -0,0 +1,109 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace System.IO
+{
+ /// <summary>
+ /// Provides static methods for converting from Win32 errors codes to exceptions, HRESULTS and error messages.
+ /// </summary>
+ internal static class Win32Marshal
+ {
+ /// <summary>
+ /// Converts, resetting it, the last Win32 error into a corresponding <see cref="Exception"/> object.
+ /// </summary>
+ internal static Exception GetExceptionForLastWin32Error()
+ {
+ int errorCode = Marshal.GetLastWin32Error();
+ return GetExceptionForWin32Error(errorCode, string.Empty);
+ }
+
+ /// <summary>
+ /// Converts the specified Win32 error into a corresponding <see cref="Exception"/> object.
+ /// </summary>
+ internal static Exception GetExceptionForWin32Error(int errorCode)
+ {
+ return GetExceptionForWin32Error(errorCode, string.Empty);
+ }
+
+ /// <summary>
+ /// Converts the specified Win32 error into a corresponding <see cref="Exception"/> object, optionally
+ /// including the specified path in the error message.
+ /// </summary>
+ internal static Exception GetExceptionForWin32Error(int errorCode, string path)
+ {
+ switch (errorCode)
+ {
+ case Interop.Errors.ERROR_FILE_NOT_FOUND:
+ if (path.Length == 0)
+ return new FileNotFoundException(SR.IO_FileNotFound);
+ else
+ return new FileNotFoundException(SR.Format(SR.IO_FileNotFound_FileName, path), path);
+
+ case Interop.Errors.ERROR_PATH_NOT_FOUND:
+ if (path.Length == 0)
+ return new DirectoryNotFoundException(SR.IO_PathNotFound_NoPathName);
+ else
+ return new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, path));
+
+ case Interop.Errors.ERROR_ACCESS_DENIED:
+ if (path.Length == 0)
+ return new UnauthorizedAccessException(SR.UnauthorizedAccess_IODenied_NoPathName);
+ else
+ return new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_IODenied_Path, path));
+
+ case Interop.Errors.ERROR_ALREADY_EXISTS:
+ if (path.Length == 0)
+ goto default;
+
+ return new IOException(SR.Format(SR.IO_AlreadyExists_Name, path), MakeHRFromErrorCode(errorCode));
+
+ case Interop.Errors.ERROR_FILENAME_EXCED_RANGE:
+ return new PathTooLongException(SR.IO_PathTooLong);
+
+ case Interop.Errors.ERROR_INVALID_PARAMETER:
+ return new IOException(GetMessage(errorCode), MakeHRFromErrorCode(errorCode));
+
+ case Interop.Errors.ERROR_SHARING_VIOLATION:
+ if (path.Length == 0)
+ return new IOException(SR.IO_SharingViolation_NoFileName, MakeHRFromErrorCode(errorCode));
+ else
+ return new IOException(SR.Format(SR.IO_SharingViolation_File, path), MakeHRFromErrorCode(errorCode));
+
+ case Interop.Errors.ERROR_FILE_EXISTS:
+ if (path.Length == 0)
+ goto default;
+
+ return new IOException(SR.Format(SR.IO_FileExists_Name, path), MakeHRFromErrorCode(errorCode));
+
+ case Interop.Errors.ERROR_OPERATION_ABORTED:
+ return new OperationCanceledException();
+
+ default:
+ return new IOException(GetMessage(errorCode), MakeHRFromErrorCode(errorCode));
+ }
+ }
+
+ /// <summary>
+ /// Returns a HRESULT for the specified Win32 error code.
+ /// </summary>
+ internal static int MakeHRFromErrorCode(int errorCode)
+ {
+ Debug.Assert((0xFFFF0000 & errorCode) == 0, "This is an HRESULT, not an error code!");
+
+ return unchecked(((int)0x80070000) | errorCode);
+ }
+
+ /// <summary>
+ /// Returns a string message for the specified Win32 error code.
+ /// </summary>
+ internal static string GetMessage(int errorCode)
+ {
+ return Interop.Kernel32.GetMessage(errorCode);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/IObservable.cs b/src/mscorlib/shared/System/IObservable.cs
new file mode 100644
index 0000000000..aabb0b8fb4
--- /dev/null
+++ b/src/mscorlib/shared/System/IObservable.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ public interface IObservable<out T>
+ {
+ IDisposable Subscribe(IObserver<T> observer);
+ }
+}
diff --git a/src/mscorlib/shared/System/IObserver.cs b/src/mscorlib/shared/System/IObserver.cs
new file mode 100644
index 0000000000..39e123de8d
--- /dev/null
+++ b/src/mscorlib/shared/System/IObserver.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ public interface IObserver<in T>
+ {
+ void OnNext(T value);
+ void OnError(Exception error);
+ void OnCompleted();
+ }
+}
diff --git a/src/mscorlib/shared/System/IProgress.cs b/src/mscorlib/shared/System/IProgress.cs
new file mode 100644
index 0000000000..724c7bdce9
--- /dev/null
+++ b/src/mscorlib/shared/System/IProgress.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ /// <summary>Defines a provider for progress updates.</summary>
+ /// <typeparam name="T">The type of progress update value.</typeparam>
+ public interface IProgress<in T>
+ {
+ /// <summary>Reports a progress update.</summary>
+ /// <param name="value">The value of the updated progress.</param>
+ void Report(T value);
+ }
+}
diff --git a/src/mscorlib/shared/System/IndexOutOfRangeException.cs b/src/mscorlib/shared/System/IndexOutOfRangeException.cs
new file mode 100644
index 0000000000..4969c2b86d
--- /dev/null
+++ b/src/mscorlib/shared/System/IndexOutOfRangeException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for invalid array indices.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public sealed class IndexOutOfRangeException : SystemException
+ {
+ public IndexOutOfRangeException()
+ : base(SR.Arg_IndexOutOfRangeException)
+ {
+ HResult = __HResults.COR_E_INDEXOUTOFRANGE;
+ }
+
+ public IndexOutOfRangeException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_INDEXOUTOFRANGE;
+ }
+
+ public IndexOutOfRangeException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_INDEXOUTOFRANGE;
+ }
+
+ internal IndexOutOfRangeException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/InsufficientExecutionStackException.cs b/src/mscorlib/shared/System/InsufficientExecutionStackException.cs
new file mode 100644
index 0000000000..b9a4a12ddd
--- /dev/null
+++ b/src/mscorlib/shared/System/InsufficientExecutionStackException.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public sealed class InsufficientExecutionStackException : SystemException
+ {
+ public InsufficientExecutionStackException()
+ : base(SR.Arg_InsufficientExecutionStackException)
+ {
+ HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK;
+ }
+
+ public InsufficientExecutionStackException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK;
+ }
+
+ public InsufficientExecutionStackException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK;
+ }
+
+ internal InsufficientExecutionStackException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/InvalidCastException.cs b/src/mscorlib/shared/System/InvalidCastException.cs
new file mode 100644
index 0000000000..01d92b2837
--- /dev/null
+++ b/src/mscorlib/shared/System/InvalidCastException.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Purpose: Exception class for invalid cast conditions!
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class InvalidCastException : SystemException
+ {
+ public InvalidCastException()
+ : base(SR.Arg_InvalidCastException)
+ {
+ HResult = __HResults.COR_E_INVALIDCAST;
+ }
+
+ public InvalidCastException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_INVALIDCAST;
+ }
+
+ public InvalidCastException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_INVALIDCAST;
+ }
+
+ public InvalidCastException(String message, int errorCode)
+ : base(message)
+ {
+ HResult = errorCode;
+ }
+
+ protected InvalidCastException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/InvalidOperationException.cs b/src/mscorlib/shared/System/InvalidOperationException.cs
new file mode 100644
index 0000000000..24a08c8089
--- /dev/null
+++ b/src/mscorlib/shared/System/InvalidOperationException.cs
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for denoting an object was in a state that
+** made calling a method illegal.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class InvalidOperationException : SystemException
+ {
+ public InvalidOperationException()
+ : base(SR.Arg_InvalidOperationException)
+ {
+ HResult = __HResults.COR_E_INVALIDOPERATION;
+ }
+
+ public InvalidOperationException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_INVALIDOPERATION;
+ }
+
+ public InvalidOperationException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_INVALIDOPERATION;
+ }
+
+ protected InvalidOperationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/InvalidProgramException.cs b/src/mscorlib/shared/System/InvalidProgramException.cs
new file mode 100644
index 0000000000..401b3a0ddb
--- /dev/null
+++ b/src/mscorlib/shared/System/InvalidProgramException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: The exception class for programs with invalid IL or bad metadata.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public sealed class InvalidProgramException : SystemException
+ {
+ public InvalidProgramException()
+ : base(SR.InvalidProgram_Default)
+ {
+ HResult = __HResults.COR_E_INVALIDPROGRAM;
+ }
+
+ public InvalidProgramException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_INVALIDPROGRAM;
+ }
+
+ public InvalidProgramException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_INVALIDPROGRAM;
+ }
+
+ internal InvalidProgramException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/InvalidTimeZoneException.cs b/src/mscorlib/shared/System/InvalidTimeZoneException.cs
new file mode 100644
index 0000000000..8f0751c426
--- /dev/null
+++ b/src/mscorlib/shared/System/InvalidTimeZoneException.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class InvalidTimeZoneException : Exception
+ {
+ public InvalidTimeZoneException()
+ {
+ }
+
+ public InvalidTimeZoneException(String message)
+ : base(message)
+ {
+ }
+
+ public InvalidTimeZoneException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ protected InvalidTimeZoneException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Lazy.cs b/src/mscorlib/shared/System/Lazy.cs
new file mode 100644
index 0000000000..55f915b65d
--- /dev/null
+++ b/src/mscorlib/shared/System/Lazy.cs
@@ -0,0 +1,561 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// --------------------------------------------------------------------------------------
+//
+// A class that provides a simple, lightweight implementation of lazy initialization,
+// obviating the need for a developer to implement a custom, thread-safe lazy initialization
+// solution.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+#pragma warning disable 0420
+
+using System.Diagnostics;
+using System.Runtime.ExceptionServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+using System.Threading;
+
+namespace System
+{
+ internal enum LazyState
+ {
+ NoneViaConstructor = 0,
+ NoneViaFactory = 1,
+ NoneException = 2,
+
+ PublicationOnlyViaConstructor = 3,
+ PublicationOnlyViaFactory = 4,
+ PublicationOnlyWait = 5,
+ PublicationOnlyException = 6,
+
+ ExecutionAndPublicationViaConstructor = 7,
+ ExecutionAndPublicationViaFactory = 8,
+ ExecutionAndPublicationException = 9,
+ }
+
+ /// <summary>
+ /// LazyHelper serves multiples purposes
+ /// - minimizing code size of Lazy&lt;T&gt; by implementing as much of the code that is not generic
+ /// this reduces generic code bloat, making faster class initialization
+ /// - contains singleton objects that are used to handle threading primitives for PublicationOnly mode
+ /// - allows for instantiation for ExecutionAndPublication so as to create an object for locking on
+ /// - holds exception information.
+ /// </summary>
+ internal class LazyHelper
+ {
+ internal readonly static LazyHelper NoneViaConstructor = new LazyHelper(LazyState.NoneViaConstructor);
+ internal readonly static LazyHelper NoneViaFactory = new LazyHelper(LazyState.NoneViaFactory);
+ internal readonly static LazyHelper PublicationOnlyViaConstructor = new LazyHelper(LazyState.PublicationOnlyViaConstructor);
+ internal readonly static LazyHelper PublicationOnlyViaFactory = new LazyHelper(LazyState.PublicationOnlyViaFactory);
+ internal readonly static LazyHelper PublicationOnlyWaitForOtherThreadToPublish = new LazyHelper(LazyState.PublicationOnlyWait);
+
+ internal LazyState State { get; }
+
+ private readonly ExceptionDispatchInfo _exceptionDispatch;
+
+ /// <summary>
+ /// Constructor that defines the state
+ /// </summary>
+ internal LazyHelper(LazyState state)
+ {
+ State = state;
+ }
+
+ /// <summary>
+ /// Constructor used for exceptions
+ /// </summary>
+ internal LazyHelper(LazyThreadSafetyMode mode, Exception exception)
+ {
+ switch(mode)
+ {
+ case LazyThreadSafetyMode.ExecutionAndPublication:
+ State = LazyState.ExecutionAndPublicationException;
+ break;
+
+ case LazyThreadSafetyMode.None:
+ State = LazyState.NoneException;
+ break;
+
+ case LazyThreadSafetyMode.PublicationOnly:
+ State = LazyState.PublicationOnlyException;
+ break;
+
+ default:
+ Debug.Assert(false, "internal constructor, this should never occur");
+ break;
+ }
+
+ _exceptionDispatch = ExceptionDispatchInfo.Capture(exception);
+ }
+
+ internal void ThrowException()
+ {
+ Debug.Assert(_exceptionDispatch != null, "execution path is invalid");
+
+ _exceptionDispatch.Throw();
+ }
+
+ private LazyThreadSafetyMode GetMode()
+ {
+ switch (State)
+ {
+ case LazyState.NoneViaConstructor:
+ case LazyState.NoneViaFactory:
+ case LazyState.NoneException:
+ return LazyThreadSafetyMode.None;
+
+ case LazyState.PublicationOnlyViaConstructor:
+ case LazyState.PublicationOnlyViaFactory:
+ case LazyState.PublicationOnlyWait:
+ case LazyState.PublicationOnlyException:
+ return LazyThreadSafetyMode.PublicationOnly;
+
+ case LazyState.ExecutionAndPublicationViaConstructor:
+ case LazyState.ExecutionAndPublicationViaFactory:
+ case LazyState.ExecutionAndPublicationException:
+ return LazyThreadSafetyMode.ExecutionAndPublication;
+
+ default:
+ Debug.Assert(false, "Invalid logic; State should always have a valid value");
+ return default(LazyThreadSafetyMode);
+ }
+ }
+
+ internal static LazyThreadSafetyMode? GetMode(LazyHelper state)
+ {
+ if (state == null)
+ return null; // we don't know the mode anymore
+ return state.GetMode();
+ }
+
+ internal static bool GetIsValueFaulted(LazyHelper state) => state?._exceptionDispatch != null;
+
+ internal static LazyHelper Create(LazyThreadSafetyMode mode, bool useDefaultConstructor)
+ {
+ switch (mode)
+ {
+ case LazyThreadSafetyMode.None:
+ return useDefaultConstructor ? NoneViaConstructor : NoneViaFactory;
+
+ case LazyThreadSafetyMode.PublicationOnly:
+ return useDefaultConstructor ? PublicationOnlyViaConstructor : PublicationOnlyViaFactory;
+
+ case LazyThreadSafetyMode.ExecutionAndPublication:
+ // we need to create an object for ExecutionAndPublication because we use Monitor-based locking
+ var state = useDefaultConstructor ? LazyState.ExecutionAndPublicationViaConstructor : LazyState.ExecutionAndPublicationViaFactory;
+ return new LazyHelper(state);
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(mode), SR.Lazy_ctor_ModeInvalid);
+ }
+ }
+
+ internal static object CreateViaDefaultConstructor(Type type)
+ {
+ try
+ {
+ return Activator.CreateInstance(type);
+ }
+ catch (MissingMethodException)
+ {
+ throw new MissingMemberException(SR.Lazy_CreateValue_NoParameterlessCtorForT);
+ }
+ }
+
+ internal static LazyThreadSafetyMode GetModeFromIsThreadSafe(bool isThreadSafe)
+ {
+ return isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None;
+ }
+ }
+
+ /// <summary>
+ /// Provides support for lazy initialization.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of element being lazily initialized.</typeparam>
+ /// <remarks>
+ /// <para>
+ /// By default, all public and protected members of <see cref="Lazy{T}"/> are thread-safe and may be used
+ /// concurrently from multiple threads. These thread-safety guarantees may be removed optionally and per instance
+ /// using parameters to the type's constructors.
+ /// </para>
+ /// </remarks>
+ [Serializable]
+ [DebuggerTypeProxy(typeof(System_LazyDebugView<>))]
+ [DebuggerDisplay("ThreadSafetyMode={Mode}, IsValueCreated={IsValueCreated}, IsValueFaulted={IsValueFaulted}, Value={ValueForDebugDisplay}")]
+ public class Lazy<T>
+ {
+ private static T CreateViaDefaultConstructor()
+ {
+ return (T)LazyHelper.CreateViaDefaultConstructor(typeof(T));
+ }
+
+ // _state, a volatile reference, is set to null after m_value has been set
+ [NonSerialized]
+ private volatile LazyHelper _state;
+
+ // we ensure that _factory when finished is set to null to allow garbage collector to clean up
+ // any referenced items
+ [NonSerialized]
+ private Func<T> _factory;
+
+ // _value eventually stores the lazily created value. It is valid when _state = null.
+ private T _value;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class that
+ /// uses <typeparamref name="T"/>'s default constructor for lazy initialization.
+ /// </summary>
+ /// <remarks>
+ /// An instance created with this constructor may be used concurrently from multiple threads.
+ /// </remarks>
+ public Lazy()
+ : this(null, LazyThreadSafetyMode.ExecutionAndPublication, useDefaultConstructor:true)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class that
+ /// uses a pre-initialized specified value.
+ /// </summary>
+ /// <remarks>
+ /// An instance created with this constructor should be usable by multiple threads
+ // concurrently.
+ /// </remarks>
+ public Lazy(T value)
+ {
+ _value = value;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class that uses a
+ /// specified initialization function.
+ /// </summary>
+ /// <param name="valueFactory">
+ /// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is
+ /// needed.
+ /// </param>
+ /// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is a null
+ /// reference (Nothing in Visual Basic).</exception>
+ /// <remarks>
+ /// An instance created with this constructor may be used concurrently from multiple threads.
+ /// </remarks>
+ public Lazy(Func<T> valueFactory)
+ : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication, useDefaultConstructor:false)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/>
+ /// class that uses <typeparamref name="T"/>'s default constructor and a specified thread-safety mode.
+ /// </summary>
+ /// <param name="isThreadSafe">true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time.
+ /// </param>
+ public Lazy(bool isThreadSafe) :
+ this(null, LazyHelper.GetModeFromIsThreadSafe(isThreadSafe), useDefaultConstructor:true)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/>
+ /// class that uses <typeparamref name="T"/>'s default constructor and a specified thread-safety mode.
+ /// </summary>
+ /// <param name="mode">The lazy thread-safety mode mode</param>
+ /// <exception cref="System.ArgumentOutOfRangeException"><paramref name="mode"/> mode contains an invalid valuee</exception>
+ public Lazy(LazyThreadSafetyMode mode) :
+ this(null, mode, useDefaultConstructor:true)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class
+ /// that uses a specified initialization function and a specified thread-safety mode.
+ /// </summary>
+ /// <param name="valueFactory">
+ /// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is needed.
+ /// </param>
+ /// <param name="isThreadSafe">true if this instance should be usable by multiple threads concurrently; false if the instance will only be used by one thread at a time.
+ /// </param>
+ /// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is
+ /// a null reference (Nothing in Visual Basic).</exception>
+ public Lazy(Func<T> valueFactory, bool isThreadSafe) :
+ this(valueFactory, LazyHelper.GetModeFromIsThreadSafe(isThreadSafe), useDefaultConstructor:false)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Lazy{T}"/> class
+ /// that uses a specified initialization function and a specified thread-safety mode.
+ /// </summary>
+ /// <param name="valueFactory">
+ /// The <see cref="T:System.Func{T}"/> invoked to produce the lazily-initialized value when it is needed.
+ /// </param>
+ /// <param name="mode">The lazy thread-safety mode.</param>
+ /// <exception cref="System.ArgumentNullException"><paramref name="valueFactory"/> is
+ /// a null reference (Nothing in Visual Basic).</exception>
+ /// <exception cref="System.ArgumentOutOfRangeException"><paramref name="mode"/> mode contains an invalid value.</exception>
+ public Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode)
+ : this(valueFactory, mode, useDefaultConstructor:false)
+ {
+ }
+
+ private Lazy(Func<T> valueFactory, LazyThreadSafetyMode mode, bool useDefaultConstructor)
+ {
+ if (valueFactory == null && !useDefaultConstructor)
+ throw new ArgumentNullException(nameof(valueFactory));
+
+ _factory = valueFactory;
+ _state = LazyHelper.Create(mode, useDefaultConstructor);
+ }
+
+ private void ViaConstructor()
+ {
+ _value = CreateViaDefaultConstructor();
+ _state = null; // volatile write, must occur after setting _value
+ }
+
+ private void ViaFactory(LazyThreadSafetyMode mode)
+ {
+ try
+ {
+ Func<T> factory = _factory;
+ if (factory == null)
+ throw new InvalidOperationException(SR.Lazy_Value_RecursiveCallsToValue);
+ _factory = null;
+
+ _value = factory();
+ _state = null; // volatile write, must occur after setting _value
+ }
+ catch (Exception exception)
+ {
+ _state = new LazyHelper(mode, exception);
+ throw;
+ }
+ }
+
+ private void ExecutionAndPublication(LazyHelper executionAndPublication, bool useDefaultConstructor)
+ {
+ lock (executionAndPublication)
+ {
+ // it's possible for multiple calls to have piled up behind the lock, so we need to check
+ // to see if the ExecutionAndPublication object is still the current implementation.
+ if (ReferenceEquals(_state, executionAndPublication))
+ {
+ if (useDefaultConstructor)
+ {
+ ViaConstructor();
+ }
+ else
+ {
+ ViaFactory(LazyThreadSafetyMode.ExecutionAndPublication);
+ }
+ }
+ }
+ }
+
+ private void PublicationOnly(LazyHelper publicationOnly, T possibleValue)
+ {
+ LazyHelper previous = Interlocked.CompareExchange(ref _state, LazyHelper.PublicationOnlyWaitForOtherThreadToPublish, publicationOnly);
+ if (previous == publicationOnly)
+ {
+ _factory = null;
+ _value = possibleValue;
+ _state = null; // volatile write, must occur after setting _value
+ }
+ }
+
+ private void PublicationOnlyViaConstructor(LazyHelper initializer)
+ {
+ PublicationOnly(initializer, CreateViaDefaultConstructor());
+ }
+
+ private void PublicationOnlyViaFactory(LazyHelper initializer)
+ {
+ Func<T> factory = _factory;
+ if (factory == null)
+ {
+ PublicationOnlyWaitForOtherThreadToPublish();
+ }
+ else
+ {
+ PublicationOnly(initializer, factory());
+ }
+ }
+
+ private void PublicationOnlyWaitForOtherThreadToPublish()
+ {
+ var spinWait = new SpinWait();
+ while (!ReferenceEquals(_state, null))
+ {
+ // We get here when PublicationOnly temporarily sets _state to LazyHelper.PublicationOnlyWaitForOtherThreadToPublish.
+ // This temporary state should be quickly followed by _state being set to null.
+ spinWait.SpinOnce();
+ }
+ }
+
+ private T CreateValue()
+ {
+ // we have to create a copy of state here, and use the copy exclusively from here on in
+ // so as to ensure thread safety.
+ var state = _state;
+ if (state != null)
+ {
+ switch (state.State)
+ {
+ case LazyState.NoneViaConstructor:
+ ViaConstructor();
+ break;
+
+ case LazyState.NoneViaFactory:
+ ViaFactory(LazyThreadSafetyMode.None);
+ break;
+
+ case LazyState.PublicationOnlyViaConstructor:
+ PublicationOnlyViaConstructor(state);
+ break;
+
+ case LazyState.PublicationOnlyViaFactory:
+ PublicationOnlyViaFactory(state);
+ break;
+
+ case LazyState.PublicationOnlyWait:
+ PublicationOnlyWaitForOtherThreadToPublish();
+ break;
+
+ case LazyState.ExecutionAndPublicationViaConstructor:
+ ExecutionAndPublication(state, useDefaultConstructor:true);
+ break;
+
+ case LazyState.ExecutionAndPublicationViaFactory:
+ ExecutionAndPublication(state, useDefaultConstructor:false);
+ break;
+
+ default:
+ state.ThrowException();
+ break;
+ }
+ }
+ return Value;
+ }
+
+ /// <summary>Forces initialization during serialization.</summary>
+ /// <param name="context">The StreamingContext for the serialization operation.</param>
+ [OnSerializing]
+ private void OnSerializing(StreamingContext context)
+ {
+ // Force initialization
+ T dummy = Value;
+ }
+
+ /// <summary>Creates and returns a string representation of this instance.</summary>
+ /// <returns>The result of calling <see cref="System.Object.ToString"/> on the <see
+ /// cref="Value"/>.</returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <see cref="Value"/> is null.
+ /// </exception>
+ public override string ToString()
+ {
+ return IsValueCreated ? Value.ToString() : SR.Lazy_ToString_ValueNotCreated;
+ }
+
+ /// <summary>Gets the value of the Lazy&lt;T&gt; for debugging display purposes.</summary>
+ internal T ValueForDebugDisplay
+ {
+ get
+ {
+ if (!IsValueCreated)
+ {
+ return default(T);
+ }
+ return _value;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance may be used concurrently from multiple threads.
+ /// </summary>
+ internal LazyThreadSafetyMode? Mode => LazyHelper.GetMode(_state);
+
+ /// <summary>
+ /// Gets whether the value creation is faulted or not
+ /// </summary>
+ internal bool IsValueFaulted => LazyHelper.GetIsValueFaulted(_state);
+
+ /// <summary>Gets a value indicating whether the <see cref="T:System.Lazy{T}"/> has been initialized.
+ /// </summary>
+ /// <value>true if the <see cref="T:System.Lazy{T}"/> instance has been initialized;
+ /// otherwise, false.</value>
+ /// <remarks>
+ /// The initialization of a <see cref="T:System.Lazy{T}"/> instance may result in either
+ /// a value being produced or an exception being thrown. If an exception goes unhandled during initialization,
+ /// <see cref="IsValueCreated"/> will return false.
+ /// </remarks>
+ public bool IsValueCreated => _state == null;
+
+ /// <summary>Gets the lazily initialized value of the current <see
+ /// cref="T:System.Threading.Lazy{T}"/>.</summary>
+ /// <value>The lazily initialized value of the current <see
+ /// cref="T:System.Threading.Lazy{T}"/>.</value>
+ /// <exception cref="T:System.MissingMemberException">
+ /// The <see cref="T:System.Threading.Lazy{T}"/> was initialized to use the default constructor
+ /// of the type being lazily initialized, and that type does not have a public, parameterless constructor.
+ /// </exception>
+ /// <exception cref="T:System.MemberAccessException">
+ /// The <see cref="T:System.Threading.Lazy{T}"/> was initialized to use the default constructor
+ /// of the type being lazily initialized, and permissions to access the constructor were missing.
+ /// </exception>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The <see cref="T:System.Threading.Lazy{T}"/> was constructed with the <see cref="T:System.Threading.LazyThreadSafetyMode.ExecutionAndPublication"/> or
+ /// <see cref="T:System.Threading.LazyThreadSafetyMode.None"/> and the initialization function attempted to access <see cref="Value"/> on this instance.
+ /// </exception>
+ /// <remarks>
+ /// If <see cref="IsValueCreated"/> is false, accessing <see cref="Value"/> will force initialization.
+ /// Please <see cref="System.Threading.LazyThreadSafetyMode"> for more information on how <see cref="T:System.Threading.Lazy{T}"/> will behave if an exception is thrown
+ /// from initialization delegate.
+ /// </remarks>
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ public T Value => _state == null ? _value : CreateValue();
+ }
+
+ /// <summary>A debugger view of the Lazy&lt;T&gt; to surface additional debugging properties and
+ /// to ensure that the Lazy&lt;T&gt; does not become initialized if it was not already.</summary>
+ internal sealed class System_LazyDebugView<T>
+ {
+ //The Lazy object being viewed.
+ private readonly Lazy<T> _lazy;
+
+ /// <summary>Constructs a new debugger view object for the provided Lazy object.</summary>
+ /// <param name="lazy">A Lazy object to browse in the debugger.</param>
+ public System_LazyDebugView(Lazy<T> lazy)
+ {
+ _lazy = lazy;
+ }
+
+ /// <summary>Returns whether the Lazy object is initialized or not.</summary>
+ public bool IsValueCreated
+ {
+ get { return _lazy.IsValueCreated; }
+ }
+
+ /// <summary>Returns the value of the Lazy object.</summary>
+ public T Value
+ {
+ get
+ { return _lazy.ValueForDebugDisplay; }
+ }
+
+ /// <summary>Returns the execution mode of the Lazy object</summary>
+ public LazyThreadSafetyMode? Mode
+ {
+ get { return _lazy.Mode; }
+ }
+
+ /// <summary>Returns the execution mode of the Lazy object</summary>
+ public bool IsValueFaulted
+ {
+ get { return _lazy.IsValueFaulted; }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/MarshalByRefObject.cs b/src/mscorlib/shared/System/MarshalByRefObject.cs
new file mode 100644
index 0000000000..1f1739b9cb
--- /dev/null
+++ b/src/mscorlib/shared/System/MarshalByRefObject.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ [Serializable]
+ public abstract class MarshalByRefObject
+ {
+ protected MarshalByRefObject()
+ {
+ }
+
+ public object GetLifetimeService()
+ {
+ throw new PlatformNotSupportedException(SR.PlatformNotSupported_Remoting);
+ }
+
+ public virtual object InitializeLifetimeService()
+ {
+ throw new PlatformNotSupportedException(SR.PlatformNotSupported_Remoting);
+ }
+
+ protected MarshalByRefObject MemberwiseClone(bool cloneIdentity)
+ {
+ MarshalByRefObject mbr = (MarshalByRefObject)base.MemberwiseClone();
+ return mbr;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/MemberAccessException.cs b/src/mscorlib/shared/System/MemberAccessException.cs
new file mode 100644
index 0000000000..54eee67b07
--- /dev/null
+++ b/src/mscorlib/shared/System/MemberAccessException.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+////////////////////////////////////////////////////////////////////////////////
+// MemberAccessException
+// Thrown when we try accessing a member that we cannot
+// access, due to it being removed, private or something similar.
+////////////////////////////////////////////////////////////////////////////////
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ // The MemberAccessException is thrown when trying to access a class
+ // member fails.
+ //
+ [Serializable]
+ public class MemberAccessException : SystemException
+ {
+ // Creates a new MemberAccessException with its message string set to
+ // the empty string, its HRESULT set to COR_E_MEMBERACCESS,
+ // and its ExceptionInfo reference set to null.
+ public MemberAccessException()
+ : base(SR.Arg_AccessException)
+ {
+ HResult = __HResults.COR_E_MEMBERACCESS;
+ }
+
+ // Creates a new MemberAccessException with its message string set to
+ // message, its HRESULT set to COR_E_ACCESS,
+ // and its ExceptionInfo reference set to null.
+ //
+ public MemberAccessException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_MEMBERACCESS;
+ }
+
+ public MemberAccessException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_MEMBERACCESS;
+ }
+
+ protected MemberAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/MethodAccessException.cs b/src/mscorlib/shared/System/MethodAccessException.cs
new file mode 100644
index 0000000000..2ecbd14d6d
--- /dev/null
+++ b/src/mscorlib/shared/System/MethodAccessException.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+** Purpose: The exception class for class loading failures.
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class MethodAccessException : MemberAccessException
+ {
+ public MethodAccessException()
+ : base(SR.Arg_MethodAccessException)
+ {
+ HResult = __HResults.COR_E_METHODACCESS;
+ }
+
+ public MethodAccessException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_METHODACCESS;
+ }
+
+ public MethodAccessException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_METHODACCESS;
+ }
+
+ protected MethodAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/MidpointRounding.cs b/src/mscorlib/shared/System/MidpointRounding.cs
new file mode 100644
index 0000000000..a75de43e65
--- /dev/null
+++ b/src/mscorlib/shared/System/MidpointRounding.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ public enum MidpointRounding
+ {
+ ToEven = 0,
+ AwayFromZero = 1,
+ }
+}
diff --git a/src/mscorlib/shared/System/MissingMethodException.cs b/src/mscorlib/shared/System/MissingMethodException.cs
new file mode 100644
index 0000000000..07d428967b
--- /dev/null
+++ b/src/mscorlib/shared/System/MissingMethodException.cs
@@ -0,0 +1,60 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: The exception class for class loading failures.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class MissingMethodException : MissingMemberException
+ {
+ public MissingMethodException()
+ : base(SR.Arg_MissingMethodException)
+ {
+ HResult = __HResults.COR_E_MISSINGMETHOD;
+ }
+
+ public MissingMethodException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_MISSINGMETHOD;
+ }
+
+ public MissingMethodException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_MISSINGMETHOD;
+ }
+
+ public MissingMethodException(string className, string methodName)
+ {
+ ClassName = className;
+ MemberName = methodName;
+ }
+
+ protected MissingMethodException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ public override string Message
+ {
+ get
+ {
+ return ClassName == null ? base.Message :
+ SR.Format(SR.MissingMethod_Name, ClassName + "." + MemberName +
+ (Signature != null ? " " + FormatSignature(Signature) : string.Empty));
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/MulticastNotSupportedException.cs b/src/mscorlib/shared/System/MulticastNotSupportedException.cs
new file mode 100644
index 0000000000..4fcaa9857d
--- /dev/null
+++ b/src/mscorlib/shared/System/MulticastNotSupportedException.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+////////////////////////////////////////////////////////////////////////////////
+// MulticastNotSupportedException
+// This is thrown when you add multiple callbacks to a non-multicast delegate.
+////////////////////////////////////////////////////////////////////////////////
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public sealed class MulticastNotSupportedException : SystemException
+ {
+ public MulticastNotSupportedException()
+ : base(SR.Arg_MulticastNotSupportedException)
+ {
+ HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED;
+ }
+
+ public MulticastNotSupportedException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED;
+ }
+
+ public MulticastNotSupportedException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_MULTICASTNOTSUPPORTED;
+ }
+
+ internal MulticastNotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/NotFiniteNumberException.cs b/src/mscorlib/shared/System/NotFiniteNumberException.cs
new file mode 100644
index 0000000000..5bc8df1e28
--- /dev/null
+++ b/src/mscorlib/shared/System/NotFiniteNumberException.cs
@@ -0,0 +1,71 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class NotFiniteNumberException : ArithmeticException
+ {
+ private double _offendingNumber;
+
+ public NotFiniteNumberException()
+ : base(SR.Arg_NotFiniteNumberException)
+ {
+ _offendingNumber = 0;
+ HResult = __HResults.COR_E_NOTFINITENUMBER;
+ }
+
+ public NotFiniteNumberException(double offendingNumber)
+ : base()
+ {
+ _offendingNumber = offendingNumber;
+ HResult = __HResults.COR_E_NOTFINITENUMBER;
+ }
+
+ public NotFiniteNumberException(String message)
+ : base(message)
+ {
+ _offendingNumber = 0;
+ HResult = __HResults.COR_E_NOTFINITENUMBER;
+ }
+
+ public NotFiniteNumberException(String message, double offendingNumber)
+ : base(message)
+ {
+ _offendingNumber = offendingNumber;
+ HResult = __HResults.COR_E_NOTFINITENUMBER;
+ }
+
+ public NotFiniteNumberException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_NOTFINITENUMBER;
+ }
+
+ public NotFiniteNumberException(String message, double offendingNumber, Exception innerException)
+ : base(message, innerException)
+ {
+ _offendingNumber = offendingNumber;
+ HResult = __HResults.COR_E_NOTFINITENUMBER;
+ }
+
+ protected NotFiniteNumberException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ _offendingNumber = info.GetInt32("OffendingNumber");
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData(info, context);
+ info.AddValue("OffendingNumber", _offendingNumber, typeof(Int32));
+ }
+
+ public double OffendingNumber
+ {
+ get { return _offendingNumber; }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/NotImplementedException.cs b/src/mscorlib/shared/System/NotImplementedException.cs
new file mode 100644
index 0000000000..4d141eac8c
--- /dev/null
+++ b/src/mscorlib/shared/System/NotImplementedException.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception thrown when a requested method or operation is not
+** implemented.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class NotImplementedException : SystemException
+ {
+ public NotImplementedException()
+ : base(SR.Arg_NotImplementedException)
+ {
+ HResult = __HResults.E_NOTIMPL;
+ }
+ public NotImplementedException(String message)
+ : base(message)
+ {
+ HResult = __HResults.E_NOTIMPL;
+ }
+ public NotImplementedException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.E_NOTIMPL;
+ }
+
+ protected NotImplementedException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/NotSupportedException.cs b/src/mscorlib/shared/System/NotSupportedException.cs
new file mode 100644
index 0000000000..21b2d54a46
--- /dev/null
+++ b/src/mscorlib/shared/System/NotSupportedException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: For methods that should be implemented on subclasses.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class NotSupportedException : SystemException
+ {
+ public NotSupportedException()
+ : base(SR.Arg_NotSupportedException)
+ {
+ HResult = __HResults.COR_E_NOTSUPPORTED;
+ }
+
+ public NotSupportedException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_NOTSUPPORTED;
+ }
+
+ public NotSupportedException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_NOTSUPPORTED;
+ }
+
+ protected NotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/NullReferenceException.cs b/src/mscorlib/shared/System/NullReferenceException.cs
new file mode 100644
index 0000000000..0aa5c6197a
--- /dev/null
+++ b/src/mscorlib/shared/System/NullReferenceException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for dereferencing a null reference.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class NullReferenceException : SystemException
+ {
+ public NullReferenceException()
+ : base(SR.Arg_NullReferenceException)
+ {
+ HResult = __HResults.COR_E_NULLREFERENCE;
+ }
+
+ public NullReferenceException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_NULLREFERENCE;
+ }
+
+ public NullReferenceException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_NULLREFERENCE;
+ }
+
+ protected NullReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/ObjectDisposedException.cs b/src/mscorlib/shared/System/ObjectDisposedException.cs
new file mode 100644
index 0000000000..abb7c89dae
--- /dev/null
+++ b/src/mscorlib/shared/System/ObjectDisposedException.cs
@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Globalization;
+using System.Runtime.Serialization;
+
+namespace System
+{
+ /// <devdoc>
+ /// <para> The exception that is thrown when accessing an object that was
+ /// disposed.</para>
+ /// </devdoc>
+ [Serializable]
+ public class ObjectDisposedException : InvalidOperationException
+ {
+ private String _objectName;
+
+ // This constructor should only be called by the EE (COMPlusThrow)
+ private ObjectDisposedException() :
+ this(null, SR.ObjectDisposed_Generic)
+ {
+ }
+
+ public ObjectDisposedException(String objectName) :
+ this(objectName, SR.ObjectDisposed_Generic)
+ {
+ }
+
+ public ObjectDisposedException(String objectName, String message) : base(message)
+ {
+ HResult = __HResults.COR_E_OBJECTDISPOSED;
+ _objectName = objectName;
+ }
+
+ public ObjectDisposedException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_OBJECTDISPOSED;
+ }
+
+ protected ObjectDisposedException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ _objectName = info.GetString("ObjectName");
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData(info, context);
+ info.AddValue("ObjectName", ObjectName, typeof(String));
+ }
+
+ /// <devdoc>
+ /// <para>Gets the text for the message for this exception.</para>
+ /// </devdoc>
+ public override String Message
+ {
+ get
+ {
+ String name = ObjectName;
+ if (name == null || name.Length == 0)
+ return base.Message;
+
+ String objectDisposed = SR.Format(SR.ObjectDisposed_ObjectName_Name, name);
+ return base.Message + Environment.NewLine + objectDisposed;
+ }
+ }
+
+ public String ObjectName
+ {
+ get
+ {
+ if ((_objectName == null)) // && !CompatibilitySwitches.IsAppEarlierThanWindowsPhone8)
+ {
+ return String.Empty;
+ }
+ return _objectName;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/ObsoleteAttribute.cs b/src/mscorlib/shared/System/ObsoleteAttribute.cs
new file mode 100644
index 0000000000..f183685998
--- /dev/null
+++ b/src/mscorlib/shared/System/ObsoleteAttribute.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Attribute for functions, etc that will be removed.
+**
+**
+===========================================================*/
+
+using System;
+
+namespace System
+{
+ // This attribute is attached to members that are not to be used any longer.
+ // Message is some human readable explanation of what to use
+ // Error indicates if the compiler should treat usage of such a method as an
+ // error. (this would be used if the actual implementation of the obsolete
+ // method's implementation had changed).
+ //
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum |
+ AttributeTargets.Interface | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Delegate
+ , Inherited = false)]
+ public sealed class ObsoleteAttribute : Attribute
+ {
+ private String _message;
+ private bool _error;
+
+ public ObsoleteAttribute()
+ {
+ _message = null;
+ _error = false;
+ }
+
+ public ObsoleteAttribute(String message)
+ {
+ _message = message;
+ _error = false;
+ }
+
+ public ObsoleteAttribute(String message, bool error)
+ {
+ _message = message;
+ _error = error;
+ }
+
+ public String Message
+ {
+ get { return _message; }
+ }
+
+ public bool IsError
+ {
+ get { return _error; }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/OverflowException.cs b/src/mscorlib/shared/System/OverflowException.cs
new file mode 100644
index 0000000000..e28c688dd6
--- /dev/null
+++ b/src/mscorlib/shared/System/OverflowException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for Arthimatic Overflows.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class OverflowException : ArithmeticException
+ {
+ public OverflowException()
+ : base(SR.Arg_OverflowException)
+ {
+ HResult = __HResults.COR_E_OVERFLOW;
+ }
+
+ public OverflowException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_OVERFLOW;
+ }
+
+ public OverflowException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_OVERFLOW;
+ }
+
+ protected OverflowException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/ParamArrayAttribute.cs b/src/mscorlib/shared/System/ParamArrayAttribute.cs
new file mode 100644
index 0000000000..d3c3d46d56
--- /dev/null
+++ b/src/mscorlib/shared/System/ParamArrayAttribute.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Attribute for multiple parameters.
+**
+**
+=============================================================================*/
+
+namespace System
+{
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
+ public sealed class ParamArrayAttribute : Attribute
+ {
+ public ParamArrayAttribute() { }
+ }
+}
diff --git a/src/mscorlib/shared/System/ParamsArray.cs b/src/mscorlib/shared/System/ParamsArray.cs
new file mode 100644
index 0000000000..1c73bc5856
--- /dev/null
+++ b/src/mscorlib/shared/System/ParamsArray.cs
@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ internal struct ParamsArray
+ {
+ // Sentinel fixed-length arrays eliminate the need for a "count" field keeping this
+ // struct down to just 4 fields. These are only used for their "Length" property,
+ // that is, their elements are never set or referenced.
+ private static readonly object[] s_oneArgArray = new object[1];
+ private static readonly object[] s_twoArgArray = new object[2];
+ private static readonly object[] s_threeArgArray = new object[3];
+
+ private readonly object _arg0;
+ private readonly object _arg1;
+ private readonly object _arg2;
+
+ // After construction, the first three elements of this array will never be accessed
+ // because the indexer will retrieve those values from arg0, arg1, and arg2.
+ private readonly object[] _args;
+
+ public ParamsArray(object arg0)
+ {
+ _arg0 = arg0;
+ _arg1 = null;
+ _arg2 = null;
+
+ // Always assign this.args to make use of its "Length" property
+ _args = s_oneArgArray;
+ }
+
+ public ParamsArray(object arg0, object arg1)
+ {
+ _arg0 = arg0;
+ _arg1 = arg1;
+ _arg2 = null;
+
+ // Always assign this.args to make use of its "Length" property
+ _args = s_twoArgArray;
+ }
+
+ public ParamsArray(object arg0, object arg1, object arg2)
+ {
+ _arg0 = arg0;
+ _arg1 = arg1;
+ _arg2 = arg2;
+
+ // Always assign this.args to make use of its "Length" property
+ _args = s_threeArgArray;
+ }
+
+ public ParamsArray(object[] args)
+ {
+ int len = args.Length;
+ _arg0 = len > 0 ? args[0] : null;
+ _arg1 = len > 1 ? args[1] : null;
+ _arg2 = len > 2 ? args[2] : null;
+ _args = args;
+ }
+
+ public int Length
+ {
+ get { return _args.Length; }
+ }
+
+ public object this[int index]
+ {
+ get { return index == 0 ? _arg0 : GetAtSlow(index); }
+ }
+
+ private object GetAtSlow(int index)
+ {
+ if (index == 1)
+ return _arg1;
+ if (index == 2)
+ return _arg2;
+ return _args[index];
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/PlatformNotSupportedException.cs b/src/mscorlib/shared/System/PlatformNotSupportedException.cs
new file mode 100644
index 0000000000..d2370b3924
--- /dev/null
+++ b/src/mscorlib/shared/System/PlatformNotSupportedException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: To handle features that don't run on particular platforms
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class PlatformNotSupportedException : NotSupportedException
+ {
+ public PlatformNotSupportedException()
+ : base(SR.Arg_PlatformNotSupported)
+ {
+ HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED;
+ }
+
+ public PlatformNotSupportedException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED;
+ }
+
+ public PlatformNotSupportedException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_PLATFORMNOTSUPPORTED;
+ }
+
+ protected PlatformNotSupportedException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Progress.cs b/src/mscorlib/shared/System/Progress.cs
new file mode 100644
index 0000000000..755e7719fe
--- /dev/null
+++ b/src/mscorlib/shared/System/Progress.cs
@@ -0,0 +1,105 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Threading;
+using System.Diagnostics;
+
+namespace System
+{
+ /// <summary>
+ /// Provides an IProgress{T} that invokes callbacks for each reported progress value.
+ /// </summary>
+ /// <typeparam name="T">Specifies the type of the progress report value.</typeparam>
+ /// <remarks>
+ /// Any handler provided to the constructor or event handlers registered with
+ /// the <see cref="ProgressChanged"/> event are invoked through a
+ /// <see cref="System.Threading.SynchronizationContext"/> instance captured
+ /// when the instance is constructed. If there is no current SynchronizationContext
+ /// at the time of construction, the callbacks will be invoked on the ThreadPool.
+ /// </remarks>
+ public class Progress<T> : IProgress<T>
+ {
+ /// <summary>The synchronization context captured upon construction. This will never be null.</summary>
+ private readonly SynchronizationContext _synchronizationContext;
+ /// <summary>The handler specified to the constructor. This may be null.</summary>
+ private readonly Action<T> _handler;
+ /// <summary>A cached delegate used to post invocation to the synchronization context.</summary>
+ private readonly SendOrPostCallback _invokeHandlers;
+
+ /// <summary>Initializes the <see cref="Progress{T}"/>.</summary>
+ public Progress()
+ {
+ // Capture the current synchronization context.
+ // If there is no current context, we use a default instance targeting the ThreadPool.
+ _synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext;
+ Debug.Assert(_synchronizationContext != null);
+ _invokeHandlers = new SendOrPostCallback(InvokeHandlers);
+ }
+
+ /// <summary>Initializes the <see cref="Progress{T}"/> with the specified callback.</summary>
+ /// <param name="handler">
+ /// A handler to invoke for each reported progress value. This handler will be invoked
+ /// in addition to any delegates registered with the <see cref="ProgressChanged"/> event.
+ /// Depending on the <see cref="System.Threading.SynchronizationContext"/> instance captured by
+ /// the <see cref="Progress"/> at construction, it's possible that this handler instance
+ /// could be invoked concurrently with itself.
+ /// </param>
+ /// <exception cref="System.ArgumentNullException">The <paramref name="handler"/> is null (Nothing in Visual Basic).</exception>
+ public Progress(Action<T> handler) : this()
+ {
+ if (handler == null) throw new ArgumentNullException(nameof(handler));
+ _handler = handler;
+ }
+
+ /// <summary>Raised for each reported progress value.</summary>
+ /// <remarks>
+ /// Handlers registered with this event will be invoked on the
+ /// <see cref="System.Threading.SynchronizationContext"/> captured when the instance was constructed.
+ /// </remarks>
+ public event EventHandler<T> ProgressChanged;
+
+ /// <summary>Reports a progress change.</summary>
+ /// <param name="value">The value of the updated progress.</param>
+ protected virtual void OnReport(T value)
+ {
+ // If there's no handler, don't bother going through the sync context.
+ // Inside the callback, we'll need to check again, in case
+ // an event handler is removed between now and then.
+ Action<T> handler = _handler;
+ EventHandler<T> changedEvent = ProgressChanged;
+ if (handler != null || changedEvent != null)
+ {
+ // Post the processing to the sync context.
+ // (If T is a value type, it will get boxed here.)
+ _synchronizationContext.Post(_invokeHandlers, value);
+ }
+ }
+
+ /// <summary>Reports a progress change.</summary>
+ /// <param name="value">The value of the updated progress.</param>
+ void IProgress<T>.Report(T value) { OnReport(value); }
+
+ /// <summary>Invokes the action and event callbacks.</summary>
+ /// <param name="state">The progress value.</param>
+ private void InvokeHandlers(object state)
+ {
+ T value = (T)state;
+
+ Action<T> handler = _handler;
+ EventHandler<T> changedEvent = ProgressChanged;
+
+ if (handler != null) handler(value);
+ if (changedEvent != null) changedEvent(this, value);
+ }
+ }
+
+ /// <summary>Holds static values for <see cref="Progress{T}"/>.</summary>
+ /// <remarks>This avoids one static instance per type T.</remarks>
+ internal static class ProgressStatics
+ {
+ /// <summary>A default synchronization context that targets the ThreadPool.</summary>
+ internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext();
+ }
+}
diff --git a/src/mscorlib/shared/System/Random.cs b/src/mscorlib/shared/System/Random.cs
new file mode 100644
index 0000000000..a66a9ea423
--- /dev/null
+++ b/src/mscorlib/shared/System/Random.cs
@@ -0,0 +1,274 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+** Purpose: A random number generator.
+**
+**
+===========================================================*/
+
+using System;
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Globalization;
+using System.Diagnostics.Contracts;
+
+namespace System
+{
+ [Serializable]
+ public class Random
+ {
+ //
+ // Private Constants
+ //
+ private const int MBIG = Int32.MaxValue;
+ private const int MSEED = 161803398;
+ private const int MZ = 0;
+
+
+ //
+ // Member Variables
+ //
+ private int _inext;
+ private int _inextp;
+ private int[] _seedArray = new int[56];
+
+ //
+ // Public Constants
+ //
+
+ //
+ // Native Declarations
+ //
+
+ //
+ // Constructors
+ //
+
+ /*=========================================================================================
+ **Action: Initializes a new instance of the Random class, using a default seed value
+ ===========================================================================================*/
+ public Random()
+ : this(GenerateSeed())
+ {
+ }
+
+ /*=========================================================================================
+ **Action: Initializes a new instance of the Random class, using a specified seed value
+ ===========================================================================================*/
+ public Random(int Seed)
+ {
+ int ii = 0;
+ int mj, mk;
+
+ //Initialize our Seed array.
+ int subtraction = (Seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(Seed);
+ mj = MSEED - subtraction;
+ _seedArray[55] = mj;
+ mk = 1;
+ for (int i = 1; i < 55; i++)
+ { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position.
+ if ((ii += 21) >= 55) ii -= 55;
+ _seedArray[ii] = mk;
+ mk = mj - mk;
+ if (mk < 0) mk += MBIG;
+ mj = _seedArray[ii];
+ }
+ for (int k = 1; k < 5; k++)
+ {
+ for (int i = 1; i < 56; i++)
+ {
+ int n = i + 30;
+ if (n >= 55) n -= 55;
+ _seedArray[i] -= _seedArray[1 + n];
+ if (_seedArray[i] < 0) _seedArray[i] += MBIG;
+ }
+ }
+ _inext = 0;
+ _inextp = 21;
+ Seed = 1;
+ }
+
+ //
+ // Package Private Methods
+ //
+
+ /*====================================Sample====================================
+ **Action: Return a new random number [0..1) and reSeed the Seed array.
+ **Returns: A double [0..1)
+ **Arguments: None
+ **Exceptions: None
+ ==============================================================================*/
+ protected virtual double Sample()
+ {
+ //Including this division at the end gives us significantly improved
+ //random number distribution.
+ return (InternalSample() * (1.0 / MBIG));
+ }
+
+ private int InternalSample()
+ {
+ int retVal;
+ int locINext = _inext;
+ int locINextp = _inextp;
+
+ if (++locINext >= 56) locINext = 1;
+ if (++locINextp >= 56) locINextp = 1;
+
+ retVal = _seedArray[locINext] - _seedArray[locINextp];
+
+ if (retVal == MBIG) retVal--;
+ if (retVal < 0) retVal += MBIG;
+
+ _seedArray[locINext] = retVal;
+
+ _inext = locINext;
+ _inextp = locINextp;
+
+ return retVal;
+ }
+
+ [ThreadStatic]
+ private static Random t_threadRandom;
+ private static readonly Random s_globalRandom = new Random(GenerateGlobalSeed());
+
+ /*=====================================GenerateSeed=====================================
+ **Returns: An integer that can be used as seed values for consecutively
+ creating lots of instances on the same thread within a short period of time.
+ ========================================================================================*/
+ private static int GenerateSeed()
+ {
+ Random rnd = t_threadRandom;
+ if (rnd == null)
+ {
+ int seed;
+ lock (s_globalRandom)
+ {
+ seed = s_globalRandom.Next();
+ }
+ rnd = new Random(seed);
+ t_threadRandom = rnd;
+ }
+ return rnd.Next();
+ }
+
+ /*==================================GenerateGlobalSeed====================================
+ **Action: Creates a number to use as global seed.
+ **Returns: An integer that is safe to use as seed values for thread-local seed generators.
+ ==========================================================================================*/
+ private static unsafe int GenerateGlobalSeed()
+ {
+ int result;
+ Interop.GetRandomBytes((byte*)&result, sizeof(int));
+ return result;
+ }
+
+ //
+ // Public Instance Methods
+ //
+
+
+ /*=====================================Next=====================================
+ **Returns: An int [0..Int32.MaxValue)
+ **Arguments: None
+ **Exceptions: None.
+ ==============================================================================*/
+ public virtual int Next()
+ {
+ return InternalSample();
+ }
+
+ private double GetSampleForLargeRange()
+ {
+ // The distribution of double value returned by Sample
+ // is not distributed well enough for a large range.
+ // If we use Sample for a range [Int32.MinValue..Int32.MaxValue)
+ // We will end up getting even numbers only.
+
+ int result = InternalSample();
+ // Note we can't use addition here. The distribution will be bad if we do that.
+ bool negative = (InternalSample() % 2 == 0) ? true : false; // decide the sign based on second sample
+ if (negative)
+ {
+ result = -result;
+ }
+ double d = result;
+ d += (Int32.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
+ d /= 2 * (uint)Int32.MaxValue - 1;
+ return d;
+ }
+
+
+ /*=====================================Next=====================================
+ **Returns: An int [minvalue..maxvalue)
+ **Arguments: minValue -- the least legal value for the Random number.
+ ** maxValue -- One greater than the greatest legal return value.
+ **Exceptions: None.
+ ==============================================================================*/
+ public virtual int Next(int minValue, int maxValue)
+ {
+ if (minValue > maxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue)));
+ }
+ Contract.EndContractBlock();
+
+ long range = (long)maxValue - minValue;
+ if (range <= (long)Int32.MaxValue)
+ {
+ return ((int)(Sample() * range) + minValue);
+ }
+ else
+ {
+ return (int)((long)(GetSampleForLargeRange() * range) + minValue);
+ }
+ }
+
+
+ /*=====================================Next=====================================
+ **Returns: An int [0..maxValue)
+ **Arguments: maxValue -- One more than the greatest legal return value.
+ **Exceptions: None.
+ ==============================================================================*/
+ public virtual int Next(int maxValue)
+ {
+ if (maxValue < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue)));
+ }
+ Contract.EndContractBlock();
+ return (int)(Sample() * maxValue);
+ }
+
+
+ /*=====================================Next=====================================
+ **Returns: A double [0..1)
+ **Arguments: None
+ **Exceptions: None
+ ==============================================================================*/
+ public virtual double NextDouble()
+ {
+ return Sample();
+ }
+
+
+ /*==================================NextBytes===================================
+ **Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled.
+ **Returns:Void
+ **Arguments: buffer -- the array to be filled.
+ **Exceptions: None
+ ==============================================================================*/
+ public virtual void NextBytes(byte[] buffer)
+ {
+ if (buffer == null) throw new ArgumentNullException(nameof(buffer));
+ Contract.EndContractBlock();
+ for (int i = 0; i < buffer.Length; i++)
+ {
+ buffer[i] = (byte)(InternalSample() % (Byte.MaxValue + 1));
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/RankException.cs b/src/mscorlib/shared/System/RankException.cs
new file mode 100644
index 0000000000..612d0f086c
--- /dev/null
+++ b/src/mscorlib/shared/System/RankException.cs
@@ -0,0 +1,42 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: For methods that are passed arrays with the wrong number of
+** dimensions.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class RankException : SystemException
+ {
+ public RankException()
+ : base(SR.Arg_RankException)
+ {
+ HResult = __HResults.COR_E_RANK;
+ }
+
+ public RankException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_RANK;
+ }
+
+ public RankException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_RANK;
+ }
+
+ protected RankException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs b/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs
new file mode 100644
index 0000000000..459a19cb71
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AmbiguousMatchException.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public sealed class AmbiguousMatchException : SystemException
+ {
+ public AmbiguousMatchException()
+ : base(SR.RFLCT_Ambiguous)
+ {
+ HResult = __HResults.COR_E_AMBIGUOUSMATCH;
+ }
+
+ public AmbiguousMatchException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_AMBIGUOUSMATCH;
+ }
+
+ public AmbiguousMatchException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_AMBIGUOUSMATCH;
+ }
+
+ internal AmbiguousMatchException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/Assembly.cs b/src/mscorlib/shared/System/Reflection/Assembly.cs
new file mode 100644
index 0000000000..d35ffc7066
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/Assembly.cs
@@ -0,0 +1,200 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.IO;
+using System.Globalization;
+using System.Collections.Generic;
+using System.Configuration.Assemblies;
+using System.Runtime.Serialization;
+using System.Security;
+
+namespace System.Reflection
+{
+ public abstract partial class Assembly : ICustomAttributeProvider, ISerializable
+ {
+ protected Assembly() { }
+
+ public virtual IEnumerable<TypeInfo> DefinedTypes
+ {
+ get
+ {
+ Type[] types = GetTypes();
+ TypeInfo[] typeinfos = new TypeInfo[types.Length];
+ for (int i = 0; i < types.Length; i++)
+ {
+ TypeInfo typeinfo = types[i].GetTypeInfo();
+ if (typeinfo == null)
+ throw new NotSupportedException(SR.Format(SR.NotSupported_NoTypeInfo, types[i].FullName));
+
+ typeinfos[i] = typeinfo;
+ }
+ return typeinfos;
+ }
+ }
+
+ public virtual Type[] GetTypes()
+ {
+ Module[] m = GetModules(false);
+
+ int finalLength = 0;
+ Type[][] moduleTypes = new Type[m.Length][];
+
+ for (int i = 0; i < moduleTypes.Length; i++)
+ {
+ moduleTypes[i] = m[i].GetTypes();
+ finalLength += moduleTypes[i].Length;
+ }
+
+ int current = 0;
+ Type[] ret = new Type[finalLength];
+ for (int i = 0; i < moduleTypes.Length; i++)
+ {
+ int length = moduleTypes[i].Length;
+ Array.Copy(moduleTypes[i], 0, ret, current, length);
+ current += length;
+ }
+
+ return ret;
+ }
+
+ public virtual IEnumerable<Type> ExportedTypes => GetExportedTypes();
+ public virtual Type[] GetExportedTypes() { throw NotImplemented.ByDesign; }
+
+ public virtual string CodeBase { get { throw NotImplemented.ByDesign; } }
+ public virtual MethodInfo EntryPoint { get { throw NotImplemented.ByDesign; } }
+ public virtual string FullName { get { throw NotImplemented.ByDesign; } }
+ public virtual string ImageRuntimeVersion { get { throw NotImplemented.ByDesign; } }
+ public virtual bool IsDynamic => false;
+ public virtual string Location { get { throw NotImplemented.ByDesign; } }
+ public virtual bool ReflectionOnly { get { throw NotImplemented.ByDesign; } }
+
+ public virtual ManifestResourceInfo GetManifestResourceInfo(string resourceName) { throw NotImplemented.ByDesign; }
+ public virtual string[] GetManifestResourceNames() { throw NotImplemented.ByDesign; }
+ public virtual Stream GetManifestResourceStream(string name) { throw NotImplemented.ByDesign; }
+ public virtual Stream GetManifestResourceStream(Type type, string name) { throw NotImplemented.ByDesign; }
+
+ public bool IsFullyTrusted => true;
+
+ public virtual AssemblyName GetName() => GetName(copiedName: false);
+ public virtual AssemblyName GetName(bool copiedName) { throw NotImplemented.ByDesign; }
+
+ public virtual Type GetType(string name) => GetType(name, throwOnError: false, ignoreCase: false);
+ public virtual Type GetType(string name, bool throwOnError) => GetType(name, throwOnError: throwOnError, ignoreCase: false);
+ public virtual Type GetType(string name, bool throwOnError, bool ignoreCase) { throw NotImplemented.ByDesign; }
+
+ public virtual bool IsDefined(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; }
+
+ public virtual IEnumerable<CustomAttributeData> CustomAttributes => GetCustomAttributesData();
+ public virtual IList<CustomAttributeData> GetCustomAttributesData() { throw NotImplemented.ByDesign; }
+
+ public virtual object[] GetCustomAttributes(bool inherit) { throw NotImplemented.ByDesign; }
+ public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; }
+
+ public virtual string EscapedCodeBase => AssemblyName.EscapeCodeBase(CodeBase);
+
+ public object CreateInstance(string typeName) => CreateInstance(typeName, false, BindingFlags.Public | BindingFlags.Instance, binder: null, args: null, culture: null, activationAttributes: null);
+ public object CreateInstance(string typeName, bool ignoreCase) => CreateInstance(typeName, ignoreCase, BindingFlags.Public | BindingFlags.Instance, binder: null, args: null, culture: null, activationAttributes: null);
+ public virtual object CreateInstance(string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture, object[] activationAttributes)
+ {
+ Type t = GetType(typeName, throwOnError: false, ignoreCase: ignoreCase);
+ if (t == null)
+ return null;
+
+ return Activator.CreateInstance(t, bindingAttr, binder, args, culture, activationAttributes);
+ }
+
+ public virtual event ModuleResolveEventHandler ModuleResolve { add { throw NotImplemented.ByDesign; } remove { throw NotImplemented.ByDesign; } }
+
+ public virtual Module ManifestModule { get { throw NotImplemented.ByDesign; } }
+ public virtual Module GetModule(string name) { throw NotImplemented.ByDesign; }
+
+ public Module[] GetModules() => GetModules(getResourceModules: false);
+ public virtual Module[] GetModules(bool getResourceModules) { throw NotImplemented.ByDesign; }
+
+ public virtual IEnumerable<Module> Modules => GetLoadedModules(getResourceModules: true);
+ public Module[] GetLoadedModules() => GetLoadedModules(getResourceModules: false);
+ public virtual Module[] GetLoadedModules(bool getResourceModules) { throw NotImplemented.ByDesign; }
+
+ public virtual AssemblyName[] GetReferencedAssemblies() { throw NotImplemented.ByDesign; }
+
+ public virtual Assembly GetSatelliteAssembly(CultureInfo culture) { throw NotImplemented.ByDesign; }
+ public virtual Assembly GetSatelliteAssembly(CultureInfo culture, Version version) { throw NotImplemented.ByDesign; }
+
+ public virtual FileStream GetFile(string name) { throw NotImplemented.ByDesign; }
+ public virtual FileStream[] GetFiles() => GetFiles(getResourceModules: false);
+ public virtual FileStream[] GetFiles(bool getResourceModules) { throw NotImplemented.ByDesign; }
+
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { throw NotImplemented.ByDesign; }
+
+ public override string ToString()
+ {
+ string displayName = FullName;
+ if (displayName == null)
+ return base.ToString();
+ else
+ return displayName;
+ }
+
+ /*
+ Returns true if the assembly was loaded from the global assembly cache.
+ */
+ public virtual bool GlobalAssemblyCache { get { throw NotImplemented.ByDesign; } }
+ public virtual Int64 HostContext { get { throw NotImplemented.ByDesign; } }
+
+ public override bool Equals(object o) => base.Equals(o);
+ public override int GetHashCode() => base.GetHashCode();
+
+ public static bool operator ==(Assembly left, Assembly right)
+ {
+ if (object.ReferenceEquals(left, right))
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Assembly left, Assembly right)
+ {
+ return !(left == right);
+ }
+
+ public static string CreateQualifiedName(string assemblyName, string typeName) => typeName + ", " + assemblyName;
+
+ public static Assembly GetAssembly(Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException(nameof(type));
+
+ Module m = type.Module;
+ if (m == null)
+ return null;
+ else
+ return m.Assembly;
+ }
+
+ public static Assembly Load(byte[] rawAssembly) => Load(rawAssembly, rawSymbolStore: null);
+
+ [Obsolete("This method has been deprecated. Please use Assembly.Load() instead. http://go.microsoft.com/fwlink/?linkid=14202")]
+ public static Assembly LoadWithPartialName(string partialName)
+ {
+ if (partialName == null)
+ throw new ArgumentNullException(nameof(partialName));
+
+ return Load(partialName);
+ }
+
+ public static Assembly UnsafeLoadFrom(string assemblyFile) => LoadFrom(assemblyFile);
+
+ public Module LoadModule(string moduleName, byte[] rawModule) => LoadModule(moduleName, rawModule, null);
+ public virtual Module LoadModule(string moduleName, byte[] rawModule, byte[] rawSymbolStore) { throw NotImplemented.ByDesign; }
+
+ public static Assembly ReflectionOnlyLoad(byte[] rawAssembly) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); }
+ public static Assembly ReflectionOnlyLoad(string assemblyString) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); }
+ public static Assembly ReflectionOnlyLoadFrom(string assemblyFile) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); }
+
+ public virtual SecurityRuleSet SecurityRuleSet => SecurityRuleSet.None;
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyAlgorithmIdAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyAlgorithmIdAttribute.cs
new file mode 100644
index 0000000000..fe24f353be
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyAlgorithmIdAttribute.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Configuration.Assemblies;
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyAlgorithmIdAttribute : Attribute
+ {
+ public AssemblyAlgorithmIdAttribute(AssemblyHashAlgorithm algorithmId)
+ {
+ AlgorithmId = (uint)algorithmId;
+ }
+
+ [CLSCompliant(false)]
+ public AssemblyAlgorithmIdAttribute(uint algorithmId)
+ {
+ AlgorithmId = algorithmId;
+ }
+
+ [CLSCompliant(false)]
+ public uint AlgorithmId { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyCompanyAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyCompanyAttribute.cs
new file mode 100644
index 0000000000..d986db60a3
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyCompanyAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyCompanyAttribute : Attribute
+ {
+ public AssemblyCompanyAttribute(string company)
+ {
+ Company = company;
+ }
+
+ public string Company { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyConfigurationAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyConfigurationAttribute.cs
new file mode 100644
index 0000000000..195c4d0ca6
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyConfigurationAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyConfigurationAttribute : Attribute
+ {
+ public AssemblyConfigurationAttribute(string configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public string Configuration { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyContentType.cs b/src/mscorlib/shared/System/Reflection/AssemblyContentType.cs
new file mode 100644
index 0000000000..2ee1a00818
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyContentType.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public enum AssemblyContentType
+ {
+ Default = 0,
+ WindowsRuntime = 1,
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyCopyrightAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyCopyrightAttribute.cs
new file mode 100644
index 0000000000..e50e19932b
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyCopyrightAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyCopyrightAttribute : Attribute
+ {
+ public AssemblyCopyrightAttribute(string copyright)
+ {
+ Copyright = copyright;
+ }
+
+ public string Copyright { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyCultureAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyCultureAttribute.cs
new file mode 100644
index 0000000000..e31c6f9c1c
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyCultureAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyCultureAttribute : Attribute
+ {
+ public AssemblyCultureAttribute(string culture)
+ {
+ Culture = culture;
+ }
+
+ public string Culture { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyDefaultAliasAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyDefaultAliasAttribute.cs
new file mode 100644
index 0000000000..ced35ed3fd
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyDefaultAliasAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyDefaultAliasAttribute : Attribute
+ {
+ public AssemblyDefaultAliasAttribute(string defaultAlias)
+ {
+ DefaultAlias = defaultAlias;
+ }
+
+ public string DefaultAlias { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyDelaySignAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyDelaySignAttribute.cs
new file mode 100644
index 0000000000..eae2cf613c
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyDelaySignAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyDelaySignAttribute : Attribute
+ {
+ public AssemblyDelaySignAttribute(bool delaySign)
+ {
+ DelaySign = delaySign;
+ }
+
+ public bool DelaySign { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyDescriptionAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyDescriptionAttribute.cs
new file mode 100644
index 0000000000..50f57c96a6
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyDescriptionAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyDescriptionAttribute : Attribute
+ {
+ public AssemblyDescriptionAttribute(string description)
+ {
+ Description = description;
+ }
+
+ public string Description { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyFileVersionAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyFileVersionAttribute.cs
new file mode 100644
index 0000000000..b5face65bc
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyFileVersionAttribute.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyFileVersionAttribute : Attribute
+ {
+ public AssemblyFileVersionAttribute(string version)
+ {
+ if (version == null)
+ throw new ArgumentNullException(nameof(version));
+ Version = version;
+ }
+
+ public string Version { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyFlagsAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyFlagsAttribute.cs
new file mode 100644
index 0000000000..103413340c
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyFlagsAttribute.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyFlagsAttribute : Attribute
+ {
+ private AssemblyNameFlags _flags;
+
+ [Obsolete("This constructor has been deprecated. Please use AssemblyFlagsAttribute(AssemblyNameFlags) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
+ [CLSCompliant(false)]
+ public AssemblyFlagsAttribute(uint flags)
+ {
+ _flags = (AssemblyNameFlags)flags;
+ }
+
+ [Obsolete("This property has been deprecated. Please use AssemblyFlags instead. http://go.microsoft.com/fwlink/?linkid=14202")]
+ [CLSCompliant(false)]
+ public uint Flags
+ {
+ get { return (uint)_flags; }
+ }
+
+ public int AssemblyFlags
+ {
+ get { return (int)_flags; }
+ }
+
+ [Obsolete("This constructor has been deprecated. Please use AssemblyFlagsAttribute(AssemblyNameFlags) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
+ public AssemblyFlagsAttribute(int assemblyFlags)
+ {
+ _flags = (AssemblyNameFlags)assemblyFlags;
+ }
+
+ public AssemblyFlagsAttribute(AssemblyNameFlags assemblyFlags)
+ {
+ _flags = assemblyFlags;
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyInformationalVersionAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyInformationalVersionAttribute.cs
new file mode 100644
index 0000000000..915b973ab9
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyInformationalVersionAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyInformationalVersionAttribute : Attribute
+ {
+ public AssemblyInformationalVersionAttribute(string informationalVersion)
+ {
+ InformationalVersion = informationalVersion;
+ }
+
+ public string InformationalVersion { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyKeyFileAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyKeyFileAttribute.cs
new file mode 100644
index 0000000000..9f7387d8af
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyKeyFileAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyKeyFileAttribute : Attribute
+ {
+ public AssemblyKeyFileAttribute(string keyFile)
+ {
+ KeyFile = keyFile;
+ }
+
+ public string KeyFile { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyKeyNameAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyKeyNameAttribute.cs
new file mode 100644
index 0000000000..4cf51754ea
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyKeyNameAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyKeyNameAttribute : Attribute
+ {
+ public AssemblyKeyNameAttribute(string keyName)
+ {
+ KeyName = keyName;
+ }
+
+ public string KeyName { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyMetadataAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyMetadataAttribute.cs
new file mode 100644
index 0000000000..de9f6351ec
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyMetadataAttribute.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
+ public sealed class AssemblyMetadataAttribute : Attribute
+ {
+ public AssemblyMetadataAttribute(string key, string value)
+ {
+ Key = key;
+ Value = value;
+ }
+
+ public string Key { get; }
+
+ public string Value { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyNameFlags.cs b/src/mscorlib/shared/System/Reflection/AssemblyNameFlags.cs
new file mode 100644
index 0000000000..d321032031
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyNameFlags.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum AssemblyNameFlags
+ {
+ None = 0x0000,
+ // Flag used to indicate that an assembly ref contains the full public key, not the compressed token.
+ // Must match afPublicKey in CorHdr.h.
+ PublicKey = 0x0001,
+ //ProcArchMask = 0x00F0, // Bits describing the processor architecture
+ // Accessible via AssemblyName.ProcessorArchitecture
+ EnableJITcompileOptimizer = 0x4000,
+ EnableJITcompileTracking = 0x8000,
+ Retargetable = 0x0100,
+ //ContentType = 0x0E00, // Bits describing the ContentType are accessible via AssemblyName.ContentType
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyProductAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyProductAttribute.cs
new file mode 100644
index 0000000000..43cb62df99
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyProductAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyProductAttribute : Attribute
+ {
+ public AssemblyProductAttribute(string product)
+ {
+ Product = product;
+ }
+
+ public string Product { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblySignatureKeyAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblySignatureKeyAttribute.cs
new file mode 100644
index 0000000000..e6ec8af1b3
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblySignatureKeyAttribute.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
+ public sealed class AssemblySignatureKeyAttribute : Attribute
+ {
+ public AssemblySignatureKeyAttribute(string publicKey, string countersignature)
+ {
+ PublicKey = publicKey;
+ Countersignature = countersignature;
+ }
+
+ public string PublicKey { get; }
+
+ public string Countersignature { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyTitleAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyTitleAttribute.cs
new file mode 100644
index 0000000000..26d7a2e66c
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyTitleAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyTitleAttribute : Attribute
+ {
+ public AssemblyTitleAttribute(string title)
+ {
+ Title = title;
+ }
+
+ public string Title { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyTrademarkAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyTrademarkAttribute.cs
new file mode 100644
index 0000000000..1d3edf51d5
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyTrademarkAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyTrademarkAttribute : Attribute
+ {
+ public AssemblyTrademarkAttribute(string trademark)
+ {
+ Trademark = trademark;
+ }
+
+ public string Trademark { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/AssemblyVersionAttribute.cs b/src/mscorlib/shared/System/Reflection/AssemblyVersionAttribute.cs
new file mode 100644
index 0000000000..b3557bac97
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/AssemblyVersionAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class AssemblyVersionAttribute : Attribute
+ {
+ public AssemblyVersionAttribute(string version)
+ {
+ Version = version;
+ }
+
+ public string Version { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/Binder.cs b/src/mscorlib/shared/System/Reflection/Binder.cs
new file mode 100644
index 0000000000..3dc5665d52
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/Binder.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Globalization;
+
+namespace System.Reflection
+{
+ public abstract class Binder
+ {
+ protected Binder() { }
+ public abstract FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture);
+ public abstract MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state);
+ public abstract object ChangeType(object value, Type type, CultureInfo culture);
+ public abstract void ReorderArgumentArray(ref object[] args, object state);
+ public abstract MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers);
+ public abstract PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers);
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/BindingFlags.cs b/src/mscorlib/shared/System/Reflection/BindingFlags.cs
new file mode 100644
index 0000000000..26c875d0f9
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/BindingFlags.cs
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum BindingFlags
+ {
+ // NOTES: We have lookup masks defined in RuntimeType and Activator. If we
+ // change the lookup values then these masks may need to change also.
+
+ // a place holder for no flag specifed
+ Default = 0x00,
+
+ // These flags indicate what to search for when binding
+ IgnoreCase = 0x01, // Ignore the case of Names while searching
+ DeclaredOnly = 0x02, // Only look at the members declared on the Type
+ Instance = 0x04, // Include Instance members in search
+ Static = 0x08, // Include Static members in search
+ Public = 0x10, // Include Public members in search
+ NonPublic = 0x20, // Include Non-Public members in search
+ FlattenHierarchy = 0x40, // Rollup the statics into the class.
+
+ // These flags are used by InvokeMember to determine
+ // what type of member we are trying to Invoke.
+ // BindingAccess = 0xFF00;
+ InvokeMethod = 0x0100,
+ CreateInstance = 0x0200,
+ GetField = 0x0400,
+ SetField = 0x0800,
+ GetProperty = 0x1000,
+ SetProperty = 0x2000,
+
+ // These flags are also used by InvokeMember but they should only
+ // be used when calling InvokeMember on a COM object.
+ PutDispProperty = 0x4000,
+ PutRefDispProperty = 0x8000,
+
+ ExactBinding = 0x010000, // Bind with Exact Type matching, No Change type
+ SuppressChangeType = 0x020000,
+
+ // DefaultValueBinding will return the set of methods having ArgCount or
+ // more parameters. This is used for default values, etc.
+ OptionalParamBinding = 0x040000,
+
+ // These are a couple of misc attributes used
+ IgnoreReturn = 0x01000000, // This is used in COM Interop
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/CallingConventions.cs b/src/mscorlib/shared/System/Reflection/CallingConventions.cs
new file mode 100644
index 0000000000..bb6d6cd809
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/CallingConventions.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// CallingConventions is a set of Bits representing the calling conventions in the system.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum CallingConventions
+ {
+ //NOTE: If you change this please update COMMember.cpp. These
+ // are defined there.
+ Standard = 0x0001,
+ VarArgs = 0x0002,
+ Any = Standard | VarArgs,
+ HasThis = 0x0020,
+ ExplicitThis = 0x0040,
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/ConstructorInfo.cs b/src/mscorlib/shared/System/Reflection/ConstructorInfo.cs
new file mode 100644
index 0000000000..3ee1dbf855
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ConstructorInfo.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Globalization;
+
+namespace System.Reflection
+{
+ public abstract partial class ConstructorInfo : MethodBase
+ {
+ protected ConstructorInfo() { }
+
+ public override MemberTypes MemberType => MemberTypes.Constructor;
+
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public object Invoke(object[] parameters) => Invoke(BindingFlags.Default, binder: null, parameters: parameters, culture: null);
+ public abstract object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture);
+
+ public override bool Equals(object obj) => base.Equals(obj);
+ public override int GetHashCode() => base.GetHashCode();
+
+ public static bool operator ==(ConstructorInfo left, ConstructorInfo right)
+ {
+ if (object.ReferenceEquals(left, right))
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(ConstructorInfo left, ConstructorInfo right) => !(left == right);
+
+ public static readonly string ConstructorName = ".ctor";
+ public static readonly string TypeConstructorName = ".cctor";
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs b/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs
new file mode 100644
index 0000000000..6e11540505
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/CustomAttributeFormatException.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public class CustomAttributeFormatException : FormatException
+ {
+ public CustomAttributeFormatException()
+ : this(SR.Arg_CustomAttributeFormatException)
+ {
+ }
+
+ public CustomAttributeFormatException(string message)
+ : this(message, null)
+ {
+ }
+
+ public CustomAttributeFormatException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_CUSTOMATTRIBUTEFORMAT;
+ }
+
+ protected CustomAttributeFormatException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/DefaultMemberAttribute.cs b/src/mscorlib/shared/System/Reflection/DefaultMemberAttribute.cs
new file mode 100644
index 0000000000..3511433713
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/DefaultMemberAttribute.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
+ public sealed class DefaultMemberAttribute : Attribute
+ {
+ // You must provide the name of the member, this is required
+ public DefaultMemberAttribute(string memberName)
+ {
+ MemberName = memberName;
+ }
+
+ // A get accessor to return the name from the attribute.
+ // NOTE: There is no setter because the name must be provided
+ // to the constructor. The name is not optional.
+ public string MemberName { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/EventAttributes.cs b/src/mscorlib/shared/System/Reflection/EventAttributes.cs
new file mode 100644
index 0000000000..fbc2972f69
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/EventAttributes.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// EventAttributes are an enum defining the attributes associated with and Event.
+// These are defined in CorHdr.h and are a combination of bits and enums.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum EventAttributes
+ {
+ None = 0x0000,
+
+ // This Enum matchs the CorEventAttr defined in CorHdr.h
+ SpecialName = 0x0200, // event is special. Name describes how.
+
+ RTSpecialName = 0x0400, // Runtime(metadata internal APIs) should check name encoding.
+
+ ReservedMask = 0x0400,
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/EventInfo.cs b/src/mscorlib/shared/System/Reflection/EventInfo.cs
new file mode 100644
index 0000000000..ccd9acf648
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/EventInfo.cs
@@ -0,0 +1,115 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+
+#if FEATURE_COMINTEROP
+using EventRegistrationToken = System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken;
+#endif //#if FEATURE_COMINTEROP
+
+namespace System.Reflection
+{
+ public abstract class EventInfo : MemberInfo
+ {
+ protected EventInfo() { }
+
+ public override MemberTypes MemberType => MemberTypes.Event;
+
+ public abstract EventAttributes Attributes { get; }
+ public bool IsSpecialName => (Attributes & EventAttributes.SpecialName) != 0;
+
+ public MethodInfo[] GetOtherMethods() => GetOtherMethods(nonPublic: false);
+ public virtual MethodInfo[] GetOtherMethods(bool nonPublic) { throw NotImplemented.ByDesign; }
+
+ public virtual MethodInfo AddMethod => GetAddMethod(nonPublic: true);
+ public virtual MethodInfo RemoveMethod => GetRemoveMethod(nonPublic: true);
+ public virtual MethodInfo RaiseMethod => GetRaiseMethod(nonPublic: true);
+
+ public MethodInfo GetAddMethod() => GetAddMethod(nonPublic: false);
+ public MethodInfo GetRemoveMethod() => GetRemoveMethod(nonPublic: false);
+ public MethodInfo GetRaiseMethod() => GetRaiseMethod(nonPublic: false);
+
+ public abstract MethodInfo GetAddMethod(bool nonPublic);
+ public abstract MethodInfo GetRemoveMethod(bool nonPublic);
+ public abstract MethodInfo GetRaiseMethod(bool nonPublic);
+
+ public virtual bool IsMulticast
+ {
+ get
+ {
+ Type cl = EventHandlerType;
+ Type mc = typeof(MulticastDelegate);
+ return mc.IsAssignableFrom(cl);
+ }
+ }
+
+ public virtual Type EventHandlerType
+ {
+ get
+ {
+ MethodInfo m = GetAddMethod(true);
+ ParameterInfo[] p = m.GetParametersNoCopy();
+ Type del = typeof(Delegate);
+ for (int i = 0; i < p.Length; i++)
+ {
+ Type c = p[i].ParameterType;
+ if (c.IsSubclassOf(del))
+ return c;
+ }
+ return null;
+ }
+ }
+
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public virtual void AddEventHandler(object target, Delegate handler)
+ {
+ MethodInfo addMethod = GetAddMethod(nonPublic: false);
+
+ if (addMethod == null)
+ throw new InvalidOperationException(SR.InvalidOperation_NoPublicAddMethod);
+
+#if FEATURE_COMINTEROP
+ if (addMethod.ReturnType == typeof(EventRegistrationToken))
+ throw new InvalidOperationException(SR.InvalidOperation_NotSupportedOnWinRTEvent);
+#endif //#if FEATURE_COMINTEROP
+
+ addMethod.Invoke(target, new object[] { handler });
+ }
+
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public virtual void RemoveEventHandler(object target, Delegate handler)
+ {
+ MethodInfo removeMethod = GetRemoveMethod(nonPublic: false);
+
+ if (removeMethod == null)
+ throw new InvalidOperationException(SR.InvalidOperation_NoPublicRemoveMethod);
+
+#if FEATURE_COMINTEROP
+ ParameterInfo[] parameters = removeMethod.GetParametersNoCopy();
+ if (parameters[0].ParameterType == typeof(EventRegistrationToken))
+ throw new InvalidOperationException(SR.InvalidOperation_NotSupportedOnWinRTEvent);
+#endif //#if FEATURE_COMINTEROP
+
+ removeMethod.Invoke(target, new object[] { handler });
+ }
+
+ public override bool Equals(object obj) => base.Equals(obj);
+ public override int GetHashCode() => base.GetHashCode();
+
+ public static bool operator ==(EventInfo left, EventInfo right)
+ {
+ if (object.ReferenceEquals(left, right))
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(EventInfo left, EventInfo right) => !(left == right);
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/ExceptionHandlingClauseOptions.cs b/src/mscorlib/shared/System/Reflection/ExceptionHandlingClauseOptions.cs
new file mode 100644
index 0000000000..46285f7c82
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ExceptionHandlingClauseOptions.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum ExceptionHandlingClauseOptions : int
+ {
+ Clause = 0x0,
+ Filter = 0x1,
+ Finally = 0x2,
+ Fault = 0x4,
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/FieldAttributes.cs b/src/mscorlib/shared/System/Reflection/FieldAttributes.cs
new file mode 100644
index 0000000000..048d0e7031
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/FieldAttributes.cs
@@ -0,0 +1,40 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ // This Enum matchs the CorFieldAttr defined in CorHdr.h
+ [Flags]
+ public enum FieldAttributes
+ {
+ // member access mask - Use this mask to retrieve accessibility information.
+ FieldAccessMask = 0x0007,
+ PrivateScope = 0x0000, // Member not referenceable.
+ Private = 0x0001, // Accessible only by the parent type.
+ FamANDAssem = 0x0002, // Accessible by sub-types only in this Assembly.
+ Assembly = 0x0003, // Accessibly by anyone in the Assembly.
+ Family = 0x0004, // Accessible only by type and sub-types.
+ FamORAssem = 0x0005, // Accessibly by sub-types anywhere, plus anyone in assembly.
+ Public = 0x0006, // Accessibly by anyone who has visibility to this scope.
+ // end member access mask
+
+ // field contract attributes.
+ Static = 0x0010, // Defined on type, else per instance.
+ InitOnly = 0x0020, // Field may only be initialized, not written to after init.
+ Literal = 0x0040, // Value is compile time constant.
+ NotSerialized = 0x0080, // Field does not have to be serialized when type is remoted.
+
+ SpecialName = 0x0200, // field is special. Name describes how.
+
+ // interop attributes
+ PinvokeImpl = 0x2000, // Implementation is forwarded through pinvoke.
+
+ RTSpecialName = 0x0400, // Runtime(metadata internal APIs) should check name encoding.
+ HasFieldMarshal = 0x1000, // Field has marshalling information.
+ HasDefault = 0x8000, // Field has default.
+ HasFieldRVA = 0x0100, // Field has RVA.
+
+ ReservedMask = 0x9500,
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/FieldInfo.cs b/src/mscorlib/shared/System/Reflection/FieldInfo.cs
new file mode 100644
index 0000000000..0863c2b019
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/FieldInfo.cs
@@ -0,0 +1,72 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Globalization;
+
+namespace System.Reflection
+{
+ public abstract partial class FieldInfo : MemberInfo
+ {
+ protected FieldInfo() { }
+
+ public override MemberTypes MemberType => MemberTypes.Field;
+
+ public abstract FieldAttributes Attributes { get; }
+ public abstract Type FieldType { get; }
+
+ public bool IsInitOnly => (Attributes & FieldAttributes.InitOnly) != 0;
+ public bool IsLiteral => (Attributes & FieldAttributes.Literal) != 0;
+ public bool IsNotSerialized => (Attributes & FieldAttributes.NotSerialized) != 0;
+ public bool IsPinvokeImpl => (Attributes & FieldAttributes.PinvokeImpl) != 0;
+ public bool IsSpecialName => (Attributes & FieldAttributes.SpecialName) != 0;
+ public bool IsStatic => (Attributes & FieldAttributes.Static) != 0;
+
+ public bool IsAssembly => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly;
+ public bool IsFamily => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family;
+ public bool IsFamilyAndAssembly => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamANDAssem;
+ public bool IsFamilyOrAssembly => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem;
+ public bool IsPrivate => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private;
+ public bool IsPublic => (Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public;
+
+ public virtual bool IsSecurityCritical => true;
+ public virtual bool IsSecuritySafeCritical => false;
+ public virtual bool IsSecurityTransparent => false;
+
+ public abstract RuntimeFieldHandle FieldHandle { get; }
+
+ public override bool Equals(object obj) => base.Equals(obj);
+ public override int GetHashCode() => base.GetHashCode();
+
+ public static bool operator ==(FieldInfo left, FieldInfo right)
+ {
+ if (object.ReferenceEquals(left, right))
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(FieldInfo left, FieldInfo right) => !(left == right);
+
+ public abstract object GetValue(object obj);
+
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public void SetValue(object obj, object value) => SetValue(obj, value, BindingFlags.Default, Type.DefaultBinder, null);
+ public abstract void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture);
+
+ [CLSCompliant(false)]
+ public virtual void SetValueDirect(TypedReference obj, object value) { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); }
+ [CLSCompliant(false)]
+ public virtual object GetValueDirect(TypedReference obj) { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); }
+
+ public virtual object GetRawConstantValue() { throw new NotSupportedException(SR.NotSupported_AbstractNonCLS); }
+
+ public virtual Type[] GetOptionalCustomModifiers() { throw NotImplemented.ByDesign; }
+ public virtual Type[] GetRequiredCustomModifiers() { throw NotImplemented.ByDesign; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/GenericParameterAttributes.cs b/src/mscorlib/shared/System/Reflection/GenericParameterAttributes.cs
new file mode 100644
index 0000000000..4b579d273e
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/GenericParameterAttributes.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum GenericParameterAttributes
+ {
+ None = 0x0000,
+ VarianceMask = 0x0003,
+ Covariant = 0x0001,
+ Contravariant = 0x0002,
+ SpecialConstraintMask = 0x001C,
+ ReferenceTypeConstraint = 0x0004,
+ NotNullableValueTypeConstraint = 0x0008,
+ DefaultConstructorConstraint = 0x0010,
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/ICustomAttributeProvider.cs b/src/mscorlib/shared/System/Reflection/ICustomAttributeProvider.cs
new file mode 100644
index 0000000000..3cae295bc4
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ICustomAttributeProvider.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public interface ICustomAttributeProvider
+ {
+ object[] GetCustomAttributes(bool inherit);
+ object[] GetCustomAttributes(Type attributeType, bool inherit);
+ bool IsDefined(Type attributeType, bool inherit);
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Reflection/IReflect.cs b/src/mscorlib/shared/System/Reflection/IReflect.cs
new file mode 100644
index 0000000000..51141ae47c
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/IReflect.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Globalization;
+
+namespace System.Reflection
+{
+ public interface IReflect
+ {
+ // Return the requested method if it is implemented by the Reflection object. The
+ // match is based upon the name and DescriptorInfo which describes the signature
+ // of the method.
+ MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers);
+
+ // Return the requested method if it is implemented by the Reflection object. The
+ // match is based upon the name of the method. If the object implementes multiple methods
+ // with the same name an AmbiguousMatchException is thrown.
+ MethodInfo GetMethod(string name, BindingFlags bindingAttr);
+
+ MethodInfo[] GetMethods(BindingFlags bindingAttr);
+
+ // Return the requestion field if it is implemented by the Reflection object. The
+ // match is based upon a name. There cannot be more than a single field with
+ // a name.
+ FieldInfo GetField(string name, BindingFlags bindingAttr);
+
+ FieldInfo[] GetFields(BindingFlags bindingAttr);
+
+ // Return the property based upon name. If more than one property has the given
+ // name an AmbiguousMatchException will be thrown. Returns null if no property
+ // is found.
+ PropertyInfo GetProperty(string name, BindingFlags bindingAttr);
+
+ // Return the property based upon the name and Descriptor info describing the property
+ // indexing. Return null if no property is found.
+ PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers);
+
+ // Returns an array of PropertyInfos for all the properties defined on
+ // the Reflection object.
+ PropertyInfo[] GetProperties(BindingFlags bindingAttr);
+
+ // Return an array of members which match the passed in name.
+ MemberInfo[] GetMember(string name, BindingFlags bindingAttr);
+
+ // Return an array of all of the members defined for this object.
+ MemberInfo[] GetMembers(BindingFlags bindingAttr);
+
+ // Description of the Binding Process.
+ // We must invoke a method that is accessable and for which the provided
+ // parameters have the most specific match. A method may be called if
+ // 1. The number of parameters in the method declaration equals the number of
+ // arguments provided to the invocation
+ // 2. The type of each argument can be converted by the binder to the
+ // type of the type of the parameter.
+ //
+ // The binder will find all of the matching methods. These method are found based
+ // upon the type of binding requested (MethodInvoke, Get/Set Properties). The set
+ // of methods is filtered by the name, number of arguments and a set of search modifiers
+ // defined in the Binder.
+ //
+ // After the method is selected, it will be invoked. Accessability is checked
+ // at that point. The search may be control which set of methods are searched based
+ // upon the accessibility attribute associated with the method.
+ //
+ // The BindToMethod method is responsible for selecting the method to be invoked.
+ // For the default binder, the most specific method will be selected.
+ //
+ // This will invoke a specific member...
+ object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters);
+
+ // Return the underlying Type that represents the IReflect Object. For expando object,
+ // this is the (Object) IReflectInstance.GetType(). For Type object it is this.
+ Type UnderlyingSystemType { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/IReflectableType.cs b/src/mscorlib/shared/System/Reflection/IReflectableType.cs
new file mode 100644
index 0000000000..5e2c0edab4
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/IReflectableType.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public interface IReflectableType
+ {
+ TypeInfo GetTypeInfo();
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/ImageFileMachine.cs b/src/mscorlib/shared/System/Reflection/ImageFileMachine.cs
new file mode 100644
index 0000000000..230bc952e5
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ImageFileMachine.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public enum ImageFileMachine
+ {
+ I386 = 0x014c,
+ IA64 = 0x0200,
+ AMD64 = 0x8664,
+ ARM = 0x01c4,
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/InterfaceMapping.cs b/src/mscorlib/shared/System/Reflection/InterfaceMapping.cs
new file mode 100644
index 0000000000..2e0c0d8a28
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/InterfaceMapping.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public struct InterfaceMapping
+ {
+ public Type TargetType; // The type implementing the interface
+ public Type InterfaceType; // The type representing the interface
+ public MethodInfo[] TargetMethods; // The methods implementing the interface
+ public MethodInfo[] InterfaceMethods; // The methods defined on the interface
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/IntrospectionExtensions.cs b/src/mscorlib/shared/System/Reflection/IntrospectionExtensions.cs
new file mode 100644
index 0000000000..6a18fdaa72
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/IntrospectionExtensions.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+
+namespace System.Reflection
+{
+ public static class IntrospectionExtensions
+ {
+ public static TypeInfo GetTypeInfo(this Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException(nameof(type));
+
+ return ((IReflectableType)type).GetTypeInfo(); // Unguarded cast is unbecoming but kept for compatibility.
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs b/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs
new file mode 100644
index 0000000000..e3f882c409
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/InvalidFilterCriteriaException.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public class InvalidFilterCriteriaException : ApplicationException
+ {
+ public InvalidFilterCriteriaException()
+ : this(SR.Arg_InvalidFilterCriteriaException)
+ {
+ }
+
+ public InvalidFilterCriteriaException(string message)
+ : this(message, null)
+ {
+ }
+
+ public InvalidFilterCriteriaException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_INVALIDFILTERCRITERIA;
+ }
+
+ protected InvalidFilterCriteriaException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/ManifestResourceInfo.cs b/src/mscorlib/shared/System/Reflection/ManifestResourceInfo.cs
new file mode 100644
index 0000000000..b9c56ab857
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ManifestResourceInfo.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public class ManifestResourceInfo
+ {
+ public ManifestResourceInfo(Assembly containingAssembly,
+ string containingFileName,
+ ResourceLocation resourceLocation)
+ {
+ ReferencedAssembly = containingAssembly;
+ FileName = containingFileName;
+ ResourceLocation = resourceLocation;
+ }
+
+ public virtual Assembly ReferencedAssembly { get; }
+ public virtual string FileName { get; }
+ public virtual ResourceLocation ResourceLocation { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/MemberFilter.cs b/src/mscorlib/shared/System/Reflection/MemberFilter.cs
new file mode 100644
index 0000000000..bb1b15796a
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/MemberFilter.cs
@@ -0,0 +1,8 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public delegate bool MemberFilter(MemberInfo m, object filterCriteria);
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Reflection/MemberInfo.cs b/src/mscorlib/shared/System/Reflection/MemberInfo.cs
new file mode 100644
index 0000000000..1275cc15a0
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/MemberInfo.cs
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+
+namespace System.Reflection
+{
+ public abstract partial class MemberInfo : ICustomAttributeProvider
+ {
+ protected MemberInfo() { }
+
+ public abstract MemberTypes MemberType { get; }
+ public abstract string Name { get; }
+ public abstract Type DeclaringType { get; }
+ public abstract Type ReflectedType { get; }
+
+ public virtual Module Module
+ {
+ get
+ {
+ // This check is necessary because for some reason, Type adds a new "Module" property that hides the inherited one instead
+ // of overriding.
+
+ Type type = this as Type;
+ if (type != null)
+ return type.Module;
+
+ throw NotImplemented.ByDesign;
+ }
+ }
+
+ public abstract bool IsDefined(Type attributeType, bool inherit);
+ public abstract object[] GetCustomAttributes(bool inherit);
+ public abstract object[] GetCustomAttributes(Type attributeType, bool inherit);
+
+ public virtual IEnumerable<CustomAttributeData> CustomAttributes => GetCustomAttributesData();
+ public virtual IList<CustomAttributeData> GetCustomAttributesData() { throw NotImplemented.ByDesign; }
+
+ public virtual int MetadataToken { get { throw new InvalidOperationException(); } }
+
+ public override bool Equals(object obj) => base.Equals(obj);
+ public override int GetHashCode() => base.GetHashCode();
+
+ public static bool operator ==(MemberInfo left, MemberInfo right)
+ {
+ if (object.ReferenceEquals(left, right))
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ Type type1, type2;
+ MethodBase method1, method2;
+ FieldInfo field1, field2;
+ EventInfo event1, event2;
+ PropertyInfo property1, property2;
+
+ if ((type1 = left as Type) != null && (type2 = right as Type) != null)
+ return type1 == type2;
+ else if ((method1 = left as MethodBase) != null && (method2 = right as MethodBase) != null)
+ return method1 == method2;
+ else if ((field1 = left as FieldInfo) != null && (field2 = right as FieldInfo) != null)
+ return field1 == field2;
+ else if ((event1 = left as EventInfo) != null && (event2 = right as EventInfo) != null)
+ return event1 == event2;
+ else if ((property1 = left as PropertyInfo) != null && (property2 = right as PropertyInfo) != null)
+ return property1 == property2;
+
+ return false;
+ }
+
+ public static bool operator !=(MemberInfo left, MemberInfo right) => !(left == right);
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/MemberInfoSerializationHolder.cs b/src/mscorlib/shared/System/Reflection/MemberInfoSerializationHolder.cs
new file mode 100644
index 0000000000..dfc56667bd
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/MemberInfoSerializationHolder.cs
@@ -0,0 +1,315 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+using System.Globalization;
+using System.Diagnostics.Contracts;
+
+namespace System.Reflection
+{
+ [Serializable]
+#if CORECLR
+ internal
+#else
+ public // On CoreRT, this must be public because of the Reflection.Core/CoreLib divide and the need to whitelist past the ReflectionBlock.
+#endif
+ class MemberInfoSerializationHolder : ISerializable, IObjectReference
+ {
+ #region Staitc Public Members
+ public static void GetSerializationInfo(SerializationInfo info, FieldInfo f)
+ {
+ // Compat: Serializing ToString() since the full framework does it but the deserialization logic makes no use of it.
+ GetSerializationInfo(info, f.Name, f.ReflectedType, f.ToString(), MemberTypes.Field);
+ }
+
+ public static void GetSerializationInfo(SerializationInfo info, EventInfo e)
+ {
+ GetSerializationInfo(info, e.Name, e.ReflectedType, null, MemberTypes.Event);
+ }
+
+ public static void GetSerializationInfo(SerializationInfo info, ConstructorInfo c)
+ {
+ GetSerializationInfo(info, c.Name, c.ReflectedType, c.ToString(), c.SerializationToString(), MemberTypes.Constructor, genericArguments: null);
+ }
+
+ public static void GetSerializationInfo(SerializationInfo info, MethodInfo m)
+ {
+ Type[] genericArguments = m.IsConstructedGenericMethod ? m.GetGenericArguments() : null;
+ GetSerializationInfo(info, m.Name, m.ReflectedType, m.ToString(), m.SerializationToString(), MemberTypes.Method, genericArguments);
+ }
+
+ public static void GetSerializationInfo(SerializationInfo info, PropertyInfo p)
+ {
+ GetSerializationInfo(info, p.Name, p.ReflectedType, p.ToString(), p.SerializationToString(), MemberTypes.Property, genericArguments: null);
+ }
+ #endregion
+
+ #region Private Static Members
+ private static void GetSerializationInfo(SerializationInfo info, string name, Type reflectedClass, string signature, MemberTypes type)
+ {
+ GetSerializationInfo(info, name, reflectedClass, signature, null, type, null);
+ }
+
+ private static void GetSerializationInfo(
+ SerializationInfo info,
+ string name,
+ Type reflectedClass,
+ string signature,
+ string signature2,
+ MemberTypes type,
+ Type[] genericArguments)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ string assemblyName = reflectedClass.Module.Assembly.FullName;
+ string typeName = reflectedClass.FullName;
+
+ info.SetType(typeof(MemberInfoSerializationHolder));
+ info.AddValue("Name", name, typeof(string));
+ info.AddValue("AssemblyName", assemblyName, typeof(string));
+ info.AddValue("ClassName", typeName, typeof(string));
+ info.AddValue("Signature", signature, typeof(string));
+ info.AddValue("Signature2", signature2, typeof(string));
+ info.AddValue("MemberType", (int)type);
+ info.AddValue("GenericArguments", genericArguments, typeof(Type[]));
+ }
+ #endregion
+
+ #region Private Data Members
+ private readonly string _memberName;
+ private readonly Type _reflectedType;
+ // _signature stores the ToString() representation of the member which is sometimes ambiguous.
+ // Mulitple overloads of the same methods or properties can identical ToString().
+ // _signature2 stores the SerializationToString() representation which should be unique for each member.
+ // It is only written and used by post 4.0 CLR versions.
+ private readonly string _signature;
+ private readonly string _signature2;
+ private readonly MemberTypes _memberType;
+ private readonly SerializationInfo _info;
+ #endregion
+
+ #region Constructor
+ // Needs to be public so it can be whitelisted in Reflection.
+ public MemberInfoSerializationHolder(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ string assemblyName = info.GetString("AssemblyName");
+ string typeName = info.GetString("ClassName");
+
+ if (assemblyName == null || typeName == null)
+ throw new SerializationException(SR.Serialization_InsufficientState);
+
+ Assembly assem = Assembly.Load(assemblyName);
+ _reflectedType = assem.GetType(typeName, true, false);
+ _memberName = info.GetString("Name");
+ _signature = info.GetString("Signature");
+ // Only v4.0 and later generates and consumes Signature2
+ _signature2 = (string)info.GetValueNoThrow("Signature2", typeof(string));
+ _memberType = (MemberTypes)info.GetInt32("MemberType");
+ _info = info;
+ }
+ #endregion
+
+ #region ISerializable
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ throw new NotSupportedException();
+ }
+ #endregion
+
+ #region IObjectReference
+ public virtual object GetRealObject(StreamingContext context)
+ {
+ if (_memberName == null || _reflectedType == null || _memberType == 0)
+ throw new SerializationException(SR.Serialization_InsufficientState);
+
+ BindingFlags bindingFlags =
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic |
+ BindingFlags.Static | BindingFlags.OptionalParamBinding;
+
+ switch (_memberType)
+ {
+ #region case MemberTypes.Field:
+ case MemberTypes.Field:
+ {
+ FieldInfo[] fields = _reflectedType.GetMember(_memberName, MemberTypes.Field, bindingFlags) as FieldInfo[];
+
+ if (fields.Length == 0)
+ throw new SerializationException(SR.Format(SR.Serialization_UnknownMember, _memberName));
+
+ return fields[0];
+ }
+ #endregion
+
+ #region case MemberTypes.Event:
+ case MemberTypes.Event:
+ {
+ EventInfo[] events = _reflectedType.GetMember(_memberName, MemberTypes.Event, bindingFlags) as EventInfo[];
+
+ if (events.Length == 0)
+ throw new SerializationException(SR.Format(SR.Serialization_UnknownMember, _memberName));
+
+ return events[0];
+ }
+ #endregion
+
+ #region case MemberTypes.Property:
+ case MemberTypes.Property:
+ {
+ PropertyInfo[] properties = _reflectedType.GetMember(_memberName, MemberTypes.Property, bindingFlags) as PropertyInfo[];
+
+ if (properties.Length == 0)
+ throw new SerializationException(SR.Format(SR.Serialization_UnknownMember, _memberName));
+
+ if (properties.Length == 1)
+ return properties[0];
+
+ if (properties.Length > 1)
+ {
+ for (int i = 0; i < properties.Length; i++)
+ {
+ if (_signature2 != null)
+ {
+ if (properties[i].SerializationToString().Equals(_signature2))
+ return properties[i];
+ }
+ else
+ {
+ if ((properties[i]).ToString().Equals(_signature))
+ return properties[i];
+ }
+ }
+ }
+
+ throw new SerializationException(SR.Format(SR.Serialization_UnknownMember, _memberName));
+ }
+ #endregion
+
+ #region case MemberTypes.Constructor:
+ case MemberTypes.Constructor:
+ {
+ if (_signature == null)
+ throw new SerializationException(SR.Serialization_NullSignature);
+
+ ConstructorInfo[] constructors = _reflectedType.GetMember(_memberName, MemberTypes.Constructor, bindingFlags) as ConstructorInfo[];
+
+ if (constructors.Length == 1)
+ return constructors[0];
+
+ if (constructors.Length > 1)
+ {
+ for (int i = 0; i < constructors.Length; i++)
+ {
+ if (_signature2 != null)
+ {
+ if (constructors[i].SerializationToString().Equals(_signature2))
+ return constructors[i];
+ }
+ else
+ {
+ if (constructors[i].ToString().Equals(_signature))
+ return constructors[i];
+ }
+ }
+ }
+
+ throw new SerializationException(SR.Format(SR.Serialization_UnknownMember, _memberName));
+ }
+ #endregion
+
+ #region case MemberTypes.Method:
+ case MemberTypes.Method:
+ {
+ MethodInfo methodInfo = null;
+
+ if (_signature == null)
+ throw new SerializationException(SR.Serialization_NullSignature);
+
+ Type[] genericArguments = _info.GetValueNoThrow("GenericArguments", typeof(Type[])) as Type[];
+
+ MethodInfo[] methods = _reflectedType.GetMember(_memberName, MemberTypes.Method, bindingFlags) as MethodInfo[];
+
+ if (methods.Length == 1)
+ methodInfo = methods[0];
+
+ else if (methods.Length > 1)
+ {
+ for (int i = 0; i < methods.Length; i++)
+ {
+ if (_signature2 != null)
+ {
+ if (methods[i].SerializationToString().Equals(_signature2))
+ {
+ methodInfo = methods[i];
+ break;
+ }
+ }
+ else
+ {
+ if (methods[i].ToString().Equals(_signature))
+ {
+ methodInfo = methods[i];
+ break;
+ }
+ }
+
+ // Handle generic methods specially since the signature match above probably won't work (the candidate
+ // method info hasn't been instantiated). If our target method is generic as well we can skip this.
+ if (genericArguments != null && methods[i].IsGenericMethod)
+ {
+ if (methods[i].GetGenericArguments().Length == genericArguments.Length)
+ {
+ MethodInfo candidateMethod = methods[i].MakeGenericMethod(genericArguments);
+
+ if (_signature2 != null)
+ {
+ if (candidateMethod.SerializationToString().Equals(_signature2))
+ {
+ methodInfo = candidateMethod;
+ break;
+ }
+ }
+ else
+ {
+ if (candidateMethod.ToString().Equals(_signature))
+ {
+ methodInfo = candidateMethod;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (methodInfo == null)
+ throw new SerializationException(SR.Format(SR.Serialization_UnknownMember, _memberName));
+
+ if (!methodInfo.IsGenericMethodDefinition)
+ return methodInfo;
+
+ if (genericArguments == null)
+ return methodInfo;
+
+ if (genericArguments[0] == null)
+ return null;
+
+ return methodInfo.MakeGenericMethod(genericArguments);
+ }
+ #endregion
+
+ default:
+ throw new ArgumentException(SR.Serialization_MemberTypeNotRecognized);
+ }
+ }
+ #endregion
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/MemberTypes.cs b/src/mscorlib/shared/System/Reflection/MemberTypes.cs
new file mode 100644
index 0000000000..57072dcfbe
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/MemberTypes.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum MemberTypes
+ {
+ Constructor = 0x01,
+ Event = 0x02,
+ Field = 0x04,
+ Method = 0x08,
+ Property = 0x10,
+ TypeInfo = 0x20,
+ Custom = 0x40,
+ NestedType = 0x80,
+ All = Constructor | Event | Field | Method | Property | TypeInfo | NestedType,
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Reflection/MethodAttributes.cs b/src/mscorlib/shared/System/Reflection/MethodAttributes.cs
new file mode 100644
index 0000000000..1a7c7bf154
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/MethodAttributes.cs
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum MethodAttributes
+ {
+ // NOTE: This Enum matchs the CorMethodAttr defined in CorHdr.h
+
+ // member access mask - Use this mask to retrieve accessibility information.
+ MemberAccessMask = 0x0007,
+ PrivateScope = 0x0000, // Member not referenceable.
+ Private = 0x0001, // Accessible only by the parent type.
+ FamANDAssem = 0x0002, // Accessible by sub-types only in this Assembly.
+ Assembly = 0x0003, // Accessibly by anyone in the Assembly.
+ Family = 0x0004, // Accessible only by type and sub-types.
+ FamORAssem = 0x0005, // Accessibly by sub-types anywhere, plus anyone in assembly.
+ Public = 0x0006, // Accessibly by anyone who has visibility to this scope.
+ // end member access mask
+
+ // method contract attributes.
+ Static = 0x0010, // Defined on type, else per instance.
+ Final = 0x0020, // Method may not be overridden.
+ Virtual = 0x0040, // Method virtual.
+ HideBySig = 0x0080, // Method hides by name+sig, else just by name.
+ CheckAccessOnOverride = 0x0200,
+
+ // vtable layout mask - Use this mask to retrieve vtable attributes.
+ VtableLayoutMask = 0x0100,
+ ReuseSlot = 0x0000, // The default.
+ NewSlot = 0x0100, // Method always gets a new slot in the vtable.
+ // end vtable layout mask
+
+ // method implementation attributes.
+ Abstract = 0x0400, // Method does not provide an implementation.
+ SpecialName = 0x0800, // Method is special. Name describes how.
+
+ // interop attributes
+ PinvokeImpl = 0x2000, // Implementation is forwarded through pinvoke.
+ UnmanagedExport = 0x0008, // Managed method exported via thunk to unmanaged code.
+ RTSpecialName = 0x1000, // Runtime should check name encoding.
+
+ HasSecurity = 0x4000, // Method has security associate with it.
+ RequireSecObject = 0x8000, // Method calls another method containing security code.
+
+ ReservedMask = 0xd000,
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/MethodBase.cs b/src/mscorlib/shared/System/Reflection/MethodBase.cs
new file mode 100644
index 0000000000..0037c74f4c
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/MethodBase.cs
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Globalization;
+using System.Diagnostics;
+
+namespace System.Reflection
+{
+ public abstract partial class MethodBase : MemberInfo
+ {
+ protected MethodBase() { }
+
+ public abstract ParameterInfo[] GetParameters();
+ public abstract MethodAttributes Attributes { get; }
+ public virtual MethodImplAttributes MethodImplementationFlags => GetMethodImplementationFlags();
+ public abstract MethodImplAttributes GetMethodImplementationFlags();
+ public virtual MethodBody GetMethodBody() { throw new InvalidOperationException(); }
+ public virtual CallingConventions CallingConvention => CallingConventions.Standard;
+
+ public bool IsAbstract => (Attributes & MethodAttributes.Abstract) != 0;
+ public bool IsConstructor
+ {
+ get
+ {
+ // To be backward compatible we only return true for instance RTSpecialName ctors.
+ return (this is ConstructorInfo &&
+ !IsStatic &&
+ ((Attributes & MethodAttributes.RTSpecialName) == MethodAttributes.RTSpecialName));
+ }
+ }
+ public bool IsFinal => (Attributes & MethodAttributes.Final) != 0;
+ public bool IsHideBySig => (Attributes & MethodAttributes.HideBySig) != 0;
+ public bool IsSpecialName => (Attributes & MethodAttributes.SpecialName) != 0;
+ public bool IsStatic => (Attributes & MethodAttributes.Static) != 0;
+ public bool IsVirtual => (Attributes & MethodAttributes.Virtual) != 0;
+
+ public bool IsAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Assembly;
+ public bool IsFamily => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family;
+ public bool IsFamilyAndAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem;
+ public bool IsFamilyOrAssembly => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem;
+ public bool IsPrivate => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private;
+ public bool IsPublic => (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public;
+
+ public virtual bool IsConstructedGenericMethod => IsGenericMethod && !IsGenericMethodDefinition;
+ public virtual bool IsGenericMethod => false;
+ public virtual bool IsGenericMethodDefinition => false;
+ public virtual Type[] GetGenericArguments() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+ public virtual bool ContainsGenericParameters => false;
+
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public object Invoke(object obj, object[] parameters) => Invoke(obj, BindingFlags.Default, binder: null, parameters: parameters, culture: null);
+ public abstract object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture);
+
+ public abstract RuntimeMethodHandle MethodHandle { get; }
+
+ public virtual bool IsSecurityCritical { get { throw NotImplemented.ByDesign; } }
+ public virtual bool IsSecuritySafeCritical { get { throw NotImplemented.ByDesign; } }
+ public virtual bool IsSecurityTransparent { get { throw NotImplemented.ByDesign; } }
+
+ public override bool Equals(object obj) => base.Equals(obj);
+ public override int GetHashCode() => base.GetHashCode();
+
+ public static bool operator ==(MethodBase left, MethodBase right)
+ {
+ if (object.ReferenceEquals(left, right))
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ MethodInfo method1, method2;
+ ConstructorInfo constructor1, constructor2;
+
+ if ((method1 = left as MethodInfo) != null && (method2 = right as MethodInfo) != null)
+ return method1 == method2;
+ else if ((constructor1 = left as ConstructorInfo) != null && (constructor2 = right as ConstructorInfo) != null)
+ return constructor1 == constructor2;
+
+ return false;
+ }
+
+ public static bool operator !=(MethodBase left, MethodBase right) => !(left == right);
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/MethodImplAttributes.cs b/src/mscorlib/shared/System/Reflection/MethodImplAttributes.cs
new file mode 100644
index 0000000000..a1ed326002
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/MethodImplAttributes.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ // This Enum matchs the CorMethodImpl defined in CorHdr.h
+ public enum MethodImplAttributes
+ {
+ // code impl mask
+ CodeTypeMask = 0x0003, // Flags about code type.
+ IL = 0x0000, // Method impl is IL.
+ Native = 0x0001, // Method impl is native.
+ OPTIL = 0x0002, // Method impl is OPTIL
+ Runtime = 0x0003, // Method impl is provided by the runtime.
+ // end code impl mask
+
+ // managed mask
+ ManagedMask = 0x0004, // Flags specifying whether the code is managed or unmanaged.
+ Unmanaged = 0x0004, // Method impl is unmanaged, otherwise managed.
+ Managed = 0x0000, // Method impl is managed.
+ // end managed mask
+
+ // implementation info and interop
+ ForwardRef = 0x0010, // Indicates method is not defined; used primarily in merge scenarios.
+ PreserveSig = 0x0080, // Indicates method sig is exported exactly as declared.
+
+ InternalCall = 0x1000, // Internal Call...
+
+ Synchronized = 0x0020, // Method is single threaded through the body.
+ NoInlining = 0x0008, // Method may not be inlined.
+ AggressiveInlining = 0x0100, // Method should be inlined if possible.
+ NoOptimization = 0x0040, // Method may not be optimized.
+
+ MaxMethodImplVal = 0xffff,
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/MethodInfo.cs b/src/mscorlib/shared/System/Reflection/MethodInfo.cs
new file mode 100644
index 0000000000..3f60149e87
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/MethodInfo.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public abstract class MethodInfo : MethodBase
+ {
+ protected MethodInfo() { }
+
+ public override MemberTypes MemberType => MemberTypes.Method;
+
+ public virtual ParameterInfo ReturnParameter { get { throw NotImplemented.ByDesign; } }
+ public virtual Type ReturnType { get { throw NotImplemented.ByDesign; } }
+
+ public override Type[] GetGenericArguments() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+ public virtual MethodInfo GetGenericMethodDefinition() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+ public virtual MethodInfo MakeGenericMethod(params Type[] typeArguments) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+
+ public abstract MethodInfo GetBaseDefinition();
+
+ public abstract ICustomAttributeProvider ReturnTypeCustomAttributes { get; }
+
+ public virtual Delegate CreateDelegate(Type delegateType) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+ public virtual Delegate CreateDelegate(Type delegateType, object target) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+
+ public override bool Equals(object obj) => base.Equals(obj);
+ public override int GetHashCode() => base.GetHashCode();
+
+ public static bool operator ==(MethodInfo left, MethodInfo right)
+ {
+ if (object.ReferenceEquals(left, right))
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(MethodInfo left, MethodInfo right) => !(left == right);
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/Missing.cs b/src/mscorlib/shared/System/Reflection/Missing.cs
new file mode 100644
index 0000000000..fa32d43ccb
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/Missing.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public sealed class Missing : ISerializable
+ {
+ public static readonly Missing Value = new Missing();
+
+ private Missing() { }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+
+ UnitySerializationHolder.GetUnitySerializationInfo(info, this);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/Module.cs b/src/mscorlib/shared/System/Reflection/Module.cs
new file mode 100644
index 0000000000..56f83c40d9
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/Module.cs
@@ -0,0 +1,182 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ public abstract class Module : ICustomAttributeProvider, ISerializable
+ {
+ protected Module() { }
+
+ public virtual Assembly Assembly { get { throw NotImplemented.ByDesign; } }
+ public virtual string FullyQualifiedName { get { throw NotImplemented.ByDesign; } }
+ public virtual string Name { get { throw NotImplemented.ByDesign; } }
+
+ public virtual int MDStreamVersion { get { throw NotImplemented.ByDesign; } }
+ public virtual Guid ModuleVersionId { get { throw NotImplemented.ByDesign; } }
+ public virtual string ScopeName { get { throw NotImplemented.ByDesign; } }
+ public ModuleHandle ModuleHandle => GetModuleHandleImpl();
+ protected virtual ModuleHandle GetModuleHandleImpl() => ModuleHandle.EmptyHandle; // Not an api but declared protected because of Reflection.Core/Corelib divide (when built by CoreRt)
+ public virtual void GetPEKind(out PortableExecutableKinds peKind, out ImageFileMachine machine) { throw NotImplemented.ByDesign; }
+ public virtual bool IsResource() { throw NotImplemented.ByDesign; }
+
+ public virtual bool IsDefined(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; }
+ public virtual IEnumerable<CustomAttributeData> CustomAttributes => GetCustomAttributesData();
+ public virtual IList<CustomAttributeData> GetCustomAttributesData() { throw NotImplemented.ByDesign; }
+ public virtual object[] GetCustomAttributes(bool inherit) { throw NotImplemented.ByDesign; }
+ public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; }
+
+ public MethodInfo GetMethod(string name)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
+ return GetMethodImpl(name, Module.DefaultLookup, null, CallingConventions.Any, null, null);
+ }
+
+ public MethodInfo GetMethod(string name, Type[] types) => GetMethod(name, Module.DefaultLookup, null, CallingConventions.Any, types, null);
+ public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+ if (types == null)
+ throw new ArgumentNullException(nameof(types));
+ for (int i = 0; i < types.Length; i++)
+ {
+ if (types[i] == null)
+ throw new ArgumentNullException(nameof(types));
+ }
+ return GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers);
+ }
+
+ protected virtual MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) { throw NotImplemented.ByDesign; }
+
+ public MethodInfo[] GetMethods() => GetMethods(Module.DefaultLookup);
+ public virtual MethodInfo[] GetMethods(BindingFlags bindingFlags) { throw NotImplemented.ByDesign; }
+
+ public FieldInfo GetField(string name) => GetField(name, Module.DefaultLookup);
+ public virtual FieldInfo GetField(string name, BindingFlags bindingAttr) { throw NotImplemented.ByDesign; }
+
+ public FieldInfo[] GetFields() => GetFields(Module.DefaultLookup);
+ public virtual FieldInfo[] GetFields(BindingFlags bindingFlags) { throw NotImplemented.ByDesign; }
+
+ public virtual Type[] GetTypes() { throw NotImplemented.ByDesign; }
+
+ public virtual Type GetType(string className) => GetType(className, throwOnError: false, ignoreCase: false);
+ public virtual Type GetType(string className, bool ignoreCase) => GetType(className, throwOnError: false, ignoreCase: ignoreCase);
+ public virtual Type GetType(string className, bool throwOnError, bool ignoreCase) { throw NotImplemented.ByDesign; }
+
+ public virtual Type[] FindTypes(TypeFilter filter, object filterCriteria)
+ {
+ Type[] c = GetTypes();
+ int cnt = 0;
+ for (int i = 0; i < c.Length; i++)
+ {
+ if (filter != null && !filter(c[i], filterCriteria))
+ c[i] = null;
+ else
+ cnt++;
+ }
+ if (cnt == c.Length)
+ return c;
+
+ Type[] ret = new Type[cnt];
+ cnt = 0;
+ for (int i = 0; i < c.Length; i++)
+ {
+ if (c[i] != null)
+ ret[cnt++] = c[i];
+ }
+ return ret;
+ }
+
+ public virtual int MetadataToken { get { throw NotImplemented.ByDesign; } }
+
+ public FieldInfo ResolveField(int metadataToken) => ResolveField(metadataToken, null, null);
+ public virtual FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw NotImplemented.ByDesign; }
+
+ public MemberInfo ResolveMember(int metadataToken) => ResolveMember(metadataToken, null, null);
+ public virtual MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw NotImplemented.ByDesign; }
+
+ public MethodBase ResolveMethod(int metadataToken) => ResolveMethod(metadataToken, null, null);
+ public virtual MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw NotImplemented.ByDesign; }
+
+ public virtual byte[] ResolveSignature(int metadataToken) { throw NotImplemented.ByDesign; }
+ public virtual string ResolveString(int metadataToken) { throw NotImplemented.ByDesign; }
+
+ public Type ResolveType(int metadataToken) => ResolveType(metadataToken, null, null);
+ public virtual Type ResolveType(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) { throw NotImplemented.ByDesign; }
+
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { throw NotImplemented.ByDesign; }
+
+ public override bool Equals(object o) => base.Equals(o);
+ public override int GetHashCode() => base.GetHashCode();
+
+ public static bool operator ==(Module left, Module right)
+ {
+ if (object.ReferenceEquals(left, right))
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Module left, Module right) => !(left == right);
+
+ public override string ToString() => ScopeName;
+
+ public static readonly TypeFilter FilterTypeName = FilterTypeNameImpl;
+ public static readonly TypeFilter FilterTypeNameIgnoreCase = FilterTypeNameIgnoreCaseImpl;
+
+ private const BindingFlags DefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
+
+ // FilterTypeName
+ // This method will filter the class based upon the name. It supports
+ // a trailing wild card.
+ private static bool FilterTypeNameImpl(Type cls, object filterCriteria)
+ {
+ // Check that the criteria object is a String object
+ if (filterCriteria == null || !(filterCriteria is string))
+ throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString);
+
+ string str = (string)filterCriteria;
+
+ // Check to see if this is a prefix or exact match requirement
+ if (str.Length > 0 && str[str.Length - 1] == '*')
+ {
+ str = str.Substring(0, str.Length - 1);
+ return cls.Name.StartsWith(str, StringComparison.Ordinal);
+ }
+
+ return cls.Name.Equals(str);
+ }
+
+ // FilterFieldNameIgnoreCase
+ // This method filter the Type based upon name, it ignores case.
+ private static bool FilterTypeNameIgnoreCaseImpl(Type cls, object filterCriteria)
+ {
+ // Check that the criteria object is a String object
+ if (filterCriteria == null || !(filterCriteria is string))
+ throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString);
+
+ string str = (string)filterCriteria;
+
+ // Check to see if this is a prefix or exact match requirement
+ if (str.Length > 0 && str[str.Length - 1] == '*')
+ {
+ str = str.Substring(0, str.Length - 1);
+ string name = cls.Name;
+ if (name.Length >= str.Length)
+ return (string.Compare(name, 0, str, 0, str.Length, StringComparison.OrdinalIgnoreCase) == 0);
+ else
+ return false;
+ }
+ return (string.Compare(str, cls.Name, StringComparison.OrdinalIgnoreCase) == 0);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/ModuleResolveEventHandler.cs b/src/mscorlib/shared/System/Reflection/ModuleResolveEventHandler.cs
new file mode 100644
index 0000000000..eb8926b5db
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ModuleResolveEventHandler.cs
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public delegate Module ModuleResolveEventHandler(object sender, ResolveEventArgs e);
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/ObfuscateAssemblyAttribute.cs b/src/mscorlib/shared/System/Reflection/ObfuscateAssemblyAttribute.cs
new file mode 100644
index 0000000000..f8f765ced2
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ObfuscateAssemblyAttribute.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
+ public sealed class ObfuscateAssemblyAttribute : Attribute
+ {
+ public ObfuscateAssemblyAttribute(bool assemblyIsPrivate)
+ {
+ AssemblyIsPrivate = assemblyIsPrivate;
+ }
+
+ public bool AssemblyIsPrivate { get; }
+ public bool StripAfterObfuscation { get; set; } = true;
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/ObfuscationAttribute.cs b/src/mscorlib/shared/System/Reflection/ObfuscationAttribute.cs
new file mode 100644
index 0000000000..11d93b6313
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ObfuscationAttribute.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Delegate,
+ AllowMultiple = true, Inherited = false)]
+ public sealed class ObfuscationAttribute : Attribute
+ {
+ public ObfuscationAttribute()
+ {
+ }
+
+ public bool StripAfterObfuscation { get; set; } = true;
+ public bool Exclude { get; set; } = true;
+ public bool ApplyToMembers { get; set; } = true;
+ public string Feature { get; set; } = "all";
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/ParameterAttributes.cs b/src/mscorlib/shared/System/Reflection/ParameterAttributes.cs
new file mode 100644
index 0000000000..ce195897c2
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ParameterAttributes.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ParameterAttributes is an enum defining the attributes that may be
+// associated with a Parameter. These are defined in CorHdr.h.
+
+namespace System.Reflection
+{
+ // This Enum matchs the CorParamAttr defined in CorHdr.h
+ [Flags]
+ public enum ParameterAttributes
+ {
+ None = 0x0000, // no flag is specified
+ In = 0x0001, // Param is [In]
+ Out = 0x0002, // Param is [Out]
+ Lcid = 0x0004, // Param is [lcid]
+
+ Retval = 0x0008, // Param is [Retval]
+ Optional = 0x0010, // Param is optional
+
+ HasDefault = 0x1000, // Param has default value.
+ HasFieldMarshal = 0x2000, // Param has FieldMarshal.
+
+ Reserved3 = 0x4000,
+ Reserved4 = 0x8000,
+ ReservedMask = 0xf000,
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/ParameterInfo.cs b/src/mscorlib/shared/System/Reflection/ParameterInfo.cs
new file mode 100644
index 0000000000..94bfffaa53
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ParameterInfo.cs
@@ -0,0 +1,110 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ public class ParameterInfo : ICustomAttributeProvider, IObjectReference
+ {
+ protected ParameterInfo() { }
+
+ public virtual ParameterAttributes Attributes => AttrsImpl;
+ public virtual MemberInfo Member => MemberImpl;
+ public virtual string Name => NameImpl;
+ public virtual Type ParameterType => ClassImpl;
+ public virtual int Position => PositionImpl;
+
+ public bool IsIn => (Attributes & ParameterAttributes.In) != 0;
+ public bool IsLcid => (Attributes & ParameterAttributes.Lcid) != 0;
+ public bool IsOptional => (Attributes & ParameterAttributes.Optional) != 0;
+ public bool IsOut => (Attributes & ParameterAttributes.Out) != 0;
+ public bool IsRetval => (Attributes & ParameterAttributes.Retval) != 0;
+
+ public virtual object DefaultValue { get { throw NotImplemented.ByDesign; } }
+ public virtual object RawDefaultValue { get { throw NotImplemented.ByDesign; } }
+ public virtual bool HasDefaultValue { get { throw NotImplemented.ByDesign; } }
+
+ public virtual bool IsDefined(Type attributeType, bool inherit)
+ {
+ if (attributeType == null)
+ throw new ArgumentNullException(nameof(attributeType));
+
+ return false;
+ }
+
+ public virtual IEnumerable<CustomAttributeData> CustomAttributes => GetCustomAttributesData();
+ public virtual IList<CustomAttributeData> GetCustomAttributesData() { throw NotImplemented.ByDesign; }
+
+ public virtual object[] GetCustomAttributes(bool inherit) => Array.Empty<object>();
+ public virtual object[] GetCustomAttributes(Type attributeType, bool inherit)
+ {
+ if (attributeType == null)
+ throw new ArgumentNullException(nameof(attributeType));
+
+ return Array.Empty<object>();
+ }
+
+ public virtual Type[] GetOptionalCustomModifiers() => Array.Empty<Type>();
+ public virtual Type[] GetRequiredCustomModifiers() => Array.Empty<Type>();
+
+ public virtual int MetadataToken => MetadataToken_ParamDef;
+
+ public object GetRealObject(StreamingContext context)
+ {
+ // Once all the serializable fields have come in we can set up the real
+ // instance based on just two of them (MemberImpl and PositionImpl).
+
+ if (MemberImpl == null)
+ throw new SerializationException(SR.Serialization_InsufficientState);
+
+ ParameterInfo[] args = null;
+
+ switch (MemberImpl.MemberType)
+ {
+ case MemberTypes.Constructor:
+ case MemberTypes.Method:
+ if (PositionImpl == -1)
+ {
+ if (MemberImpl.MemberType == MemberTypes.Method)
+ return ((MethodInfo)MemberImpl).ReturnParameter;
+ else
+ throw new SerializationException(SR.Serialization_BadParameterInfo);
+ }
+ else
+ {
+ args = ((MethodBase)MemberImpl).GetParametersNoCopy();
+
+ if (args != null && PositionImpl < args.Length)
+ return args[PositionImpl];
+ else
+ throw new SerializationException(SR.Serialization_BadParameterInfo);
+ }
+
+ case MemberTypes.Property:
+ args = ((PropertyInfo)MemberImpl).GetIndexParameters();
+
+ if (args != null && PositionImpl > -1 && PositionImpl < args.Length)
+ return args[PositionImpl];
+ else
+ throw new SerializationException(SR.Serialization_BadParameterInfo);
+
+ default:
+ throw new SerializationException(SR.Serialization_NoParameterInfo);
+ }
+ }
+
+ public override string ToString() => ParameterType.FormatTypeName() + " " + Name;
+
+ protected ParameterAttributes AttrsImpl;
+ protected Type ClassImpl;
+ protected object DefaultValueImpl;
+ protected MemberInfo MemberImpl;
+ protected string NameImpl;
+ protected int PositionImpl;
+
+ private const int MetadataToken_ParamDef = 0x08000000;
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/ParameterModifier.cs b/src/mscorlib/shared/System/Reflection/ParameterModifier.cs
new file mode 100644
index 0000000000..18d6cf669d
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ParameterModifier.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Serializable]
+ public struct ParameterModifier
+ {
+ private readonly bool[] _byRef;
+
+ public ParameterModifier(int parameterCount)
+ {
+ if (parameterCount <= 0)
+ throw new ArgumentException(SR.Arg_ParmArraySize);
+
+ _byRef = new bool[parameterCount];
+ }
+
+ public bool this[int index]
+ {
+ get
+ {
+ return _byRef[index];
+ }
+ set
+ {
+ _byRef[index] = value;
+ }
+ }
+
+#if CORECLR
+ internal bool[] IsByRefArray => _byRef;
+#endif
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/Pointer.cs b/src/mscorlib/shared/System/Reflection/Pointer.cs
new file mode 100644
index 0000000000..13a5efff46
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/Pointer.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ [CLSCompliant(false)]
+ public sealed unsafe class Pointer : ISerializable
+ {
+ // CoreCLR: Do not add or remove fields without updating the ReflectionPointer class in runtimehandles.h
+ private readonly void* _ptr;
+ private readonly Type _ptrType;
+
+ private Pointer(void* ptr, Type ptrType)
+ {
+ Debug.Assert(ptrType.IsRuntimeImplemented()); // CoreCLR: For CoreRT's sake, _ptrType has to be declared as "Type", but in fact, it is always a RuntimeType. Code on CoreCLR expects this.
+ _ptr = ptr;
+ _ptrType = ptrType;
+ }
+
+ private Pointer(SerializationInfo info, StreamingContext context)
+ {
+ _ptr = ((IntPtr)(info.GetValue("_ptr", typeof(IntPtr)))).ToPointer();
+ _ptrType = (Type)info.GetValue("_ptrType", typeof(Type));
+ if (!_ptrType.IsRuntimeImplemented())
+ throw new SerializationException(SR.Arg_MustBeType);
+ }
+
+ public static object Box(void* ptr, Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException(nameof(type));
+ if (!type.IsPointer)
+ throw new ArgumentException(SR.Arg_MustBePointer, nameof(ptr));
+ if (!type.IsRuntimeImplemented())
+ throw new ArgumentException(SR.Arg_MustBeType, nameof(ptr));
+
+ return new Pointer(ptr, type);
+ }
+
+ public static void* Unbox(object ptr)
+ {
+ if (!(ptr is Pointer))
+ throw new ArgumentException(SR.Arg_MustBePointer, nameof(ptr));
+ return ((Pointer)ptr)._ptr;
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue("_ptr", new IntPtr(_ptr));
+ info.AddValue("_ptrType", _ptrType);
+ }
+
+ internal Type GetPointerType() => _ptrType;
+ internal object GetPointerValue() => (IntPtr)_ptr;
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/PortableExecutableKinds.cs b/src/mscorlib/shared/System/Reflection/PortableExecutableKinds.cs
new file mode 100644
index 0000000000..79be338685
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/PortableExecutableKinds.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum PortableExecutableKinds
+ {
+ NotAPortableExecutableImage = 0x0,
+ ILOnly = 0x1,
+ Required32Bit = 0x2,
+ PE32Plus = 0x4,
+ Unmanaged32Bit = 0x8,
+ Preferred32Bit = 0x10,
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/ProcessorArchitecture.cs b/src/mscorlib/shared/System/Reflection/ProcessorArchitecture.cs
new file mode 100644
index 0000000000..becb346c4f
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ProcessorArchitecture.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public enum ProcessorArchitecture
+ {
+ None = 0x0000,
+ MSIL = 0x0001,
+ X86 = 0x0002,
+ IA64 = 0x0003,
+ Amd64 = 0x0004,
+ Arm = 0x0005
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/PropertyAttributes.cs b/src/mscorlib/shared/System/Reflection/PropertyAttributes.cs
new file mode 100644
index 0000000000..31e7a653bb
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/PropertyAttributes.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// PropertyAttributes is an enum which defines the attributes that may be associated
+// with a property. The values here are defined in Corhdr.h.
+
+namespace System.Reflection
+{
+ // This Enum matchs the CorPropertyAttr defined in CorHdr.h
+ [Flags]
+ public enum PropertyAttributes
+ {
+ None = 0x0000,
+ SpecialName = 0x0200, // property is special. Name describes how.
+
+ RTSpecialName = 0x0400, // Runtime(metadata internal APIs) should check name encoding.
+ HasDefault = 0x1000, // Property has default
+
+ Reserved2 = 0x2000,
+ Reserved3 = 0x4000,
+ Reserved4 = 0x8000,
+ ReservedMask = 0xf400,
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/PropertyInfo.cs b/src/mscorlib/shared/System/Reflection/PropertyInfo.cs
new file mode 100644
index 0000000000..ff8a02e96d
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/PropertyInfo.cs
@@ -0,0 +1,74 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Globalization;
+
+namespace System.Reflection
+{
+ public abstract class PropertyInfo : MemberInfo
+ {
+ protected PropertyInfo() { }
+
+ public override MemberTypes MemberType => MemberTypes.Property;
+
+ public abstract Type PropertyType { get; }
+ public abstract ParameterInfo[] GetIndexParameters();
+
+ public abstract PropertyAttributes Attributes { get; }
+ public bool IsSpecialName => (Attributes & PropertyAttributes.SpecialName) != 0;
+
+ public abstract bool CanRead { get; }
+ public abstract bool CanWrite { get; }
+
+ public MethodInfo[] GetAccessors() => GetAccessors(nonPublic: false);
+ public abstract MethodInfo[] GetAccessors(bool nonPublic);
+
+ public virtual MethodInfo GetMethod => GetGetMethod(nonPublic: true);
+ public MethodInfo GetGetMethod() => GetGetMethod(nonPublic: false);
+ public abstract MethodInfo GetGetMethod(bool nonPublic);
+
+ public virtual MethodInfo SetMethod => GetSetMethod(nonPublic: true);
+ public MethodInfo GetSetMethod() => GetSetMethod(nonPublic: false);
+ public abstract MethodInfo GetSetMethod(bool nonPublic);
+
+ public virtual Type[] GetOptionalCustomModifiers() => Array.Empty<Type>();
+ public virtual Type[] GetRequiredCustomModifiers() => Array.Empty<Type>();
+
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public object GetValue(object obj) => GetValue(obj, index: null);
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public virtual object GetValue(object obj, object[] index) => GetValue(obj, BindingFlags.Default, binder: null, index: index, culture: null);
+ public abstract object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture);
+
+ public virtual object GetConstantValue() { throw NotImplemented.ByDesign; }
+ public virtual object GetRawConstantValue() { throw NotImplemented.ByDesign; }
+
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public void SetValue(object obj, object value) => SetValue(obj, value, index: null);
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public virtual void SetValue(object obj, object value, object[] index) => SetValue(obj, value, BindingFlags.Default, binder: null, index: index, culture: null);
+ public abstract void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture);
+
+ public override bool Equals(object obj) => base.Equals(obj);
+ public override int GetHashCode() => base.GetHashCode();
+
+ public static bool operator ==(PropertyInfo left, PropertyInfo right)
+ {
+ if (object.ReferenceEquals(left, right))
+ return true;
+
+ if ((object)left == null || (object)right == null)
+ return false;
+
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(PropertyInfo left, PropertyInfo right) => !(left == right);
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/ReflectionContext.cs b/src/mscorlib/shared/System/Reflection/ReflectionContext.cs
new file mode 100644
index 0000000000..e9e93dab81
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ReflectionContext.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public abstract class ReflectionContext
+ {
+ protected ReflectionContext() { }
+
+ public abstract Assembly MapAssembly(Assembly assembly);
+
+ public abstract TypeInfo MapType(TypeInfo type);
+
+ public virtual TypeInfo GetTypeForObject(object value)
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+
+ return MapType(value.GetType().GetTypeInfo());
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs b/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs
new file mode 100644
index 0000000000..772620cf84
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ReflectionTypeLoadException.cs
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public sealed class ReflectionTypeLoadException : SystemException, ISerializable
+ {
+ public ReflectionTypeLoadException(Type[] classes, Exception[] exceptions)
+ : base(null)
+ {
+ Types = classes;
+ LoaderExceptions = exceptions;
+ HResult = __HResults.COR_E_REFLECTIONTYPELOAD;
+ }
+
+ public ReflectionTypeLoadException(Type[] classes, Exception[] exceptions, string message)
+ : base(message)
+ {
+ Types = classes;
+ LoaderExceptions = exceptions;
+ HResult = __HResults.COR_E_REFLECTIONTYPELOAD;
+ }
+
+ internal ReflectionTypeLoadException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ Types = (Type[])(info.GetValue("Types", typeof(Type[])));
+ LoaderExceptions = (Exception[])(info.GetValue("Exceptions", typeof(Exception[])));
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData(info, context);
+ info.AddValue("Types", Types, typeof(Type[]));
+ info.AddValue("Exceptions", LoaderExceptions, typeof(Exception[]));
+ }
+
+ public Type[] Types { get; }
+
+ public Exception[] LoaderExceptions { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/ResourceAttributes.cs b/src/mscorlib/shared/System/Reflection/ResourceAttributes.cs
new file mode 100644
index 0000000000..2d03f42ba0
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ResourceAttributes.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum ResourceAttributes
+ {
+ Public = 0x0001,
+ Private = 0x0002,
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/ResourceLocation.cs b/src/mscorlib/shared/System/Reflection/ResourceLocation.cs
new file mode 100644
index 0000000000..4902333ac0
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/ResourceLocation.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ [Flags]
+ public enum ResourceLocation
+ {
+ ContainedInAnotherAssembly = 2,
+ ContainedInManifestFile = 4,
+ Embedded = 1,
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Reflection/StrongNameKeyPair.cs b/src/mscorlib/shared/System/Reflection/StrongNameKeyPair.cs
new file mode 100644
index 0000000000..c04ddd6d1a
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/StrongNameKeyPair.cs
@@ -0,0 +1,74 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.IO;
+using System.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public class StrongNameKeyPair : IDeserializationCallback, ISerializable
+ {
+ private bool _keyPairExported;
+ private byte[] _keyPairArray;
+ private string _keyPairContainer;
+ private byte[] _publicKey;
+
+ // Build key pair from file.
+ public StrongNameKeyPair(FileStream keyPairFile)
+ {
+ if (keyPairFile == null)
+ throw new ArgumentNullException(nameof(keyPairFile));
+
+ int length = (int)keyPairFile.Length;
+ _keyPairArray = new byte[length];
+ keyPairFile.Read(_keyPairArray, 0, length);
+
+ _keyPairExported = true;
+ }
+
+ // Build key pair from byte array in memory.
+ public StrongNameKeyPair(byte[] keyPairArray)
+ {
+ if (keyPairArray == null)
+ throw new ArgumentNullException(nameof(keyPairArray));
+
+ _keyPairArray = new byte[keyPairArray.Length];
+ Array.Copy(keyPairArray, _keyPairArray, keyPairArray.Length);
+
+ _keyPairExported = true;
+ }
+
+ protected StrongNameKeyPair(SerializationInfo info, StreamingContext context)
+ {
+ _keyPairExported = (bool)info.GetValue("_keyPairExported", typeof(bool));
+ _keyPairArray = (byte[])info.GetValue("_keyPairArray", typeof(byte[]));
+ _keyPairContainer = (string)info.GetValue("_keyPairContainer", typeof(string));
+ _publicKey = (byte[])info.GetValue("_publicKey", typeof(byte[]));
+ }
+
+ public StrongNameKeyPair(string keyPairContainer)
+ {
+ throw new PlatformNotSupportedException(SR.PlatformNotSupported_StrongNameSigning);
+ }
+
+ public byte[] PublicKey
+ {
+ get
+ {
+ throw new PlatformNotSupportedException(SR.PlatformNotSupported_StrongNameSigning);
+ }
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue("_keyPairExported", _keyPairExported);
+ info.AddValue("_keyPairArray", _keyPairArray);
+ info.AddValue("_keyPairContainer", _keyPairContainer);
+ info.AddValue("_publicKey", _publicKey);
+ }
+
+ void IDeserializationCallback.OnDeserialization(object sender) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/TargetException.cs b/src/mscorlib/shared/System/Reflection/TargetException.cs
new file mode 100644
index 0000000000..03f8730cdd
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/TargetException.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public class TargetException : ApplicationException
+ {
+ public TargetException()
+ : this(null)
+ {
+ }
+
+ public TargetException(string message)
+ : this(message, null)
+ {
+ }
+
+ public TargetException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_TARGET;
+ }
+
+ protected TargetException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs b/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs
new file mode 100644
index 0000000000..e934e5bde7
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/TargetInvocationException.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public sealed class TargetInvocationException : ApplicationException
+ {
+ public TargetInvocationException(Exception inner)
+ : base(SR.Arg_TargetInvocationException, inner)
+ {
+ HResult = __HResults.COR_E_TARGETINVOCATION;
+ }
+
+ public TargetInvocationException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_TARGETINVOCATION;
+ }
+
+ internal TargetInvocationException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs b/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs
new file mode 100644
index 0000000000..c3604548e6
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/TargetParameterCountException.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public sealed class TargetParameterCountException : ApplicationException
+ {
+ public TargetParameterCountException()
+ : base(SR.Arg_TargetParameterCountException)
+ {
+ HResult = __HResults.COR_E_TARGETPARAMCOUNT;
+ }
+
+ public TargetParameterCountException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_TARGETPARAMCOUNT;
+ }
+
+ public TargetParameterCountException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_TARGETPARAMCOUNT;
+ }
+
+ internal TargetParameterCountException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/TypeAttributes.cs b/src/mscorlib/shared/System/Reflection/TypeAttributes.cs
new file mode 100644
index 0000000000..aa30331856
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/TypeAttributes.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.InteropServices;
+
+namespace System.Reflection
+{
+ // This Enum matchs the CorTypeAttr defined in CorHdr.h
+ [Flags]
+ public enum TypeAttributes
+ {
+ VisibilityMask = 0x00000007,
+ NotPublic = 0x00000000, // Class is not public scope.
+ Public = 0x00000001, // Class is public scope.
+ NestedPublic = 0x00000002, // Class is nested with public visibility.
+ NestedPrivate = 0x00000003, // Class is nested with private visibility.
+ NestedFamily = 0x00000004, // Class is nested with family visibility.
+ NestedAssembly = 0x00000005, // Class is nested with assembly visibility.
+ NestedFamANDAssem = 0x00000006, // Class is nested with family and assembly visibility.
+ NestedFamORAssem = 0x00000007, // Class is nested with family or assembly visibility.
+
+ // Use this mask to retrieve class layout informaiton
+ // 0 is AutoLayout, 0x2 is SequentialLayout, 4 is ExplicitLayout
+ LayoutMask = 0x00000018,
+ AutoLayout = 0x00000000, // Class fields are auto-laid out
+ SequentialLayout = 0x00000008, // Class fields are laid out sequentially
+ ExplicitLayout = 0x00000010, // Layout is supplied explicitly
+ // end layout mask
+
+ // Use this mask to distinguish whether a type declaration is an interface. (Class vs. ValueType done based on whether it subclasses S.ValueType)
+ ClassSemanticsMask = 0x00000020,
+ Class = 0x00000000, // Type is a class (or a value type).
+ Interface = 0x00000020, // Type is an interface.
+
+ // Special semantics in addition to class semantics.
+ Abstract = 0x00000080, // Class is abstract
+ Sealed = 0x00000100, // Class is concrete and may not be extended
+ SpecialName = 0x00000400, // Class name is special. Name describes how.
+
+ // Implementation attributes.
+ Import = 0x00001000, // Class / interface is imported
+ Serializable = 0x00002000, // The class is Serializable.
+ WindowsRuntime = 0x00004000, // Type is a Windows Runtime type.
+
+ // Use tdStringFormatMask to retrieve string information for native interop
+ StringFormatMask = 0x00030000,
+ AnsiClass = 0x00000000, // LPTSTR is interpreted as ANSI in this class
+ UnicodeClass = 0x00010000, // LPTSTR is interpreted as UNICODE
+ AutoClass = 0x00020000, // LPTSTR is interpreted automatically
+ CustomFormatClass = 0x00030000, // A non-standard encoding specified by CustomFormatMask
+ CustomFormatMask = 0x00C00000, // Use this mask to retrieve non-standard encoding information for native interop. The meaning of the values of these 2 bits is unspecified.
+
+ // end string format mask
+
+ BeforeFieldInit = 0x00100000, // Initialize the class any time before first static field access.
+
+ RTSpecialName = 0x00000800, // Runtime should check name encoding.
+ HasSecurity = 0x00040000, // Class has security associate with it.
+
+ ReservedMask = 0x00040800,
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/TypeDelegator.cs b/src/mscorlib/shared/System/Reflection/TypeDelegator.cs
new file mode 100644
index 0000000000..7f928d2486
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/TypeDelegator.cs
@@ -0,0 +1,124 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// TypeDelegator
+//
+// This class wraps a Type object and delegates all methods to that Type.
+
+using CultureInfo = System.Globalization.CultureInfo;
+
+namespace System.Reflection
+{
+ [Serializable]
+ public class TypeDelegator : TypeInfo
+ {
+ public override bool IsAssignableFrom(TypeInfo typeInfo)
+ {
+ if (typeInfo == null)
+ return false;
+ return IsAssignableFrom(typeInfo.AsType());
+ }
+
+ protected Type typeImpl;
+
+ protected TypeDelegator() { }
+
+ public TypeDelegator(Type delegatingType)
+ {
+ if (delegatingType == null)
+ throw new ArgumentNullException(nameof(delegatingType));
+
+ typeImpl = delegatingType;
+ }
+
+ public override Guid GUID => typeImpl.GUID;
+ public override int MetadataToken => typeImpl.MetadataToken;
+
+ public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target,
+ object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
+ {
+ return typeImpl.InvokeMember(name, invokeAttr, binder, target, args, modifiers, culture, namedParameters);
+ }
+
+ public override Module Module => typeImpl.Module;
+ public override Assembly Assembly => typeImpl.Assembly;
+ public override RuntimeTypeHandle TypeHandle => typeImpl.TypeHandle;
+ public override string Name => typeImpl.Name;
+ public override string FullName => typeImpl.FullName;
+ public override string Namespace => typeImpl.Namespace;
+ public override string AssemblyQualifiedName => typeImpl.AssemblyQualifiedName;
+ public override Type BaseType => typeImpl.BaseType;
+
+ protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder,
+ CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
+ {
+ return typeImpl.GetConstructor(bindingAttr, binder, callConvention, types, modifiers);
+ }
+
+ public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => typeImpl.GetConstructors(bindingAttr);
+
+ protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder,
+ CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
+ {
+ // This is interesting there are two paths into the impl. One that validates
+ // type as non-null and one where type may be null.
+ if (types == null)
+ return typeImpl.GetMethod(name, bindingAttr);
+ else
+ return typeImpl.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers);
+ }
+
+ public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => typeImpl.GetMethods(bindingAttr);
+
+ public override FieldInfo GetField(string name, BindingFlags bindingAttr) => typeImpl.GetField(name, bindingAttr);
+ public override FieldInfo[] GetFields(BindingFlags bindingAttr) => typeImpl.GetFields(bindingAttr);
+
+ public override Type GetInterface(string name, bool ignoreCase) => typeImpl.GetInterface(name, ignoreCase);
+
+ public override Type[] GetInterfaces() => typeImpl.GetInterfaces();
+
+ public override EventInfo GetEvent(string name, BindingFlags bindingAttr) => typeImpl.GetEvent(name, bindingAttr);
+
+ public override EventInfo[] GetEvents() => typeImpl.GetEvents();
+
+ protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder,
+ Type returnType, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (returnType == null && types == null)
+ return typeImpl.GetProperty(name, bindingAttr);
+ else
+ return typeImpl.GetProperty(name, bindingAttr, binder, returnType, types, modifiers);
+ }
+
+ public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => typeImpl.GetProperties(bindingAttr);
+ public override EventInfo[] GetEvents(BindingFlags bindingAttr) => typeImpl.GetEvents(bindingAttr);
+ public override Type[] GetNestedTypes(BindingFlags bindingAttr) => typeImpl.GetNestedTypes(bindingAttr);
+ public override Type GetNestedType(string name, BindingFlags bindingAttr) => typeImpl.GetNestedType(name, bindingAttr);
+ public override MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) => typeImpl.GetMember(name, type, bindingAttr);
+ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => typeImpl.GetMembers(bindingAttr);
+
+ protected override TypeAttributes GetAttributeFlagsImpl() => typeImpl.Attributes;
+
+ public override bool IsSZArray => typeImpl.IsSZArray;
+
+ protected override bool IsArrayImpl() => typeImpl.IsArray;
+ protected override bool IsPrimitiveImpl() => typeImpl.IsPrimitive;
+ protected override bool IsByRefImpl() => typeImpl.IsByRef;
+ protected override bool IsPointerImpl() => typeImpl.IsPointer;
+ protected override bool IsValueTypeImpl() => typeImpl.IsValueType;
+ protected override bool IsCOMObjectImpl() => typeImpl.IsCOMObject;
+ public override bool IsConstructedGenericType => typeImpl.IsConstructedGenericType;
+ public override Type GetElementType() => typeImpl.GetElementType();
+ protected override bool HasElementTypeImpl() => typeImpl.HasElementType;
+
+ public override Type UnderlyingSystemType => typeImpl.UnderlyingSystemType;
+
+ // ICustomAttributeProvider
+ public override object[] GetCustomAttributes(bool inherit) => typeImpl.GetCustomAttributes(inherit);
+ public override object[] GetCustomAttributes(Type attributeType, bool inherit) => typeImpl.GetCustomAttributes(attributeType, inherit);
+
+ public override bool IsDefined(Type attributeType, bool inherit) => typeImpl.IsDefined(attributeType, inherit);
+ public override InterfaceMapping GetInterfaceMap(Type interfaceType) => typeImpl.GetInterfaceMap(interfaceType);
+ }
+}
diff --git a/src/mscorlib/shared/System/Reflection/TypeFilter.cs b/src/mscorlib/shared/System/Reflection/TypeFilter.cs
new file mode 100644
index 0000000000..eb049f81f9
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/TypeFilter.cs
@@ -0,0 +1,8 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Reflection
+{
+ public delegate bool TypeFilter(Type m, object filterCriteria);
+}
diff --git a/src/mscorlib/shared/System/Reflection/TypeInfo.cs b/src/mscorlib/shared/System/Reflection/TypeInfo.cs
new file mode 100644
index 0000000000..f4add736f4
--- /dev/null
+++ b/src/mscorlib/shared/System/Reflection/TypeInfo.cs
@@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+
+namespace System.Reflection
+{
+ public abstract partial class TypeInfo : Type, IReflectableType
+ {
+ protected TypeInfo() { }
+
+ TypeInfo IReflectableType.GetTypeInfo() => this;
+ public virtual Type AsType() => this;
+
+ public virtual Type[] GenericTypeParameters => IsGenericTypeDefinition ? GetGenericArguments() : Type.EmptyTypes;
+
+ public virtual EventInfo GetDeclaredEvent(string name) => GetEvent(name, TypeInfo.DeclaredOnlyLookup);
+ public virtual FieldInfo GetDeclaredField(string name) => GetField(name, TypeInfo.DeclaredOnlyLookup);
+ public virtual MethodInfo GetDeclaredMethod(string name) => GetMethod(name, TypeInfo.DeclaredOnlyLookup);
+ public virtual TypeInfo GetDeclaredNestedType(string name) => GetNestedType(name, TypeInfo.DeclaredOnlyLookup)?.GetTypeInfo();
+ public virtual PropertyInfo GetDeclaredProperty(string name) => GetProperty(name, TypeInfo.DeclaredOnlyLookup);
+
+ public virtual IEnumerable<MethodInfo> GetDeclaredMethods(string name)
+ {
+ foreach (MethodInfo method in GetMethods(TypeInfo.DeclaredOnlyLookup))
+ {
+ if (method.Name == name)
+ yield return method;
+ }
+ }
+
+ public virtual IEnumerable<ConstructorInfo> DeclaredConstructors => GetConstructors(TypeInfo.DeclaredOnlyLookup);
+ public virtual IEnumerable<EventInfo> DeclaredEvents => GetEvents(TypeInfo.DeclaredOnlyLookup);
+ public virtual IEnumerable<FieldInfo> DeclaredFields => GetFields(TypeInfo.DeclaredOnlyLookup);
+ public virtual IEnumerable<MemberInfo> DeclaredMembers => GetMembers(TypeInfo.DeclaredOnlyLookup);
+ public virtual IEnumerable<MethodInfo> DeclaredMethods => GetMethods(TypeInfo.DeclaredOnlyLookup);
+ public virtual IEnumerable<System.Reflection.TypeInfo> DeclaredNestedTypes
+ {
+ get
+ {
+ foreach (Type t in GetNestedTypes(TypeInfo.DeclaredOnlyLookup))
+ {
+ yield return t.GetTypeInfo();
+ }
+ }
+ }
+ public virtual IEnumerable<PropertyInfo> DeclaredProperties => GetProperties(TypeInfo.DeclaredOnlyLookup);
+
+ public virtual IEnumerable<Type> ImplementedInterfaces => GetInterfaces();
+
+ //a re-implementation of ISAF from Type, skipping the use of UnderlyingType
+ public virtual bool IsAssignableFrom(TypeInfo typeInfo)
+ {
+ if (typeInfo == null)
+ return false;
+
+ if (this == typeInfo)
+ return true;
+
+ // If c is a subclass of this class, then c can be cast to this type.
+ if (typeInfo.IsSubclassOf(this))
+ return true;
+
+ if (this.IsInterface)
+ {
+ return typeInfo.ImplementInterface(this);
+ }
+ else if (IsGenericParameter)
+ {
+ Type[] constraints = GetGenericParameterConstraints();
+ for (int i = 0; i < constraints.Length; i++)
+ if (!constraints[i].IsAssignableFrom(typeInfo))
+ return false;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
+ }
+}
diff --git a/src/mscorlib/shared/System/ResolveEventArgs.cs b/src/mscorlib/shared/System/ResolveEventArgs.cs
new file mode 100644
index 0000000000..6196947bb5
--- /dev/null
+++ b/src/mscorlib/shared/System/ResolveEventArgs.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Reflection;
+
+namespace System
+{
+ public class ResolveEventArgs : EventArgs
+ {
+ public ResolveEventArgs(string name)
+ {
+ Name = name;
+ }
+
+ public ResolveEventArgs(string name, Assembly requestingAssembly)
+ {
+ Name = name;
+ RequestingAssembly = requestingAssembly;
+ }
+
+ public string Name { get; }
+ public Assembly RequestingAssembly { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/ResolveEventHandler.cs b/src/mscorlib/shared/System/ResolveEventHandler.cs
new file mode 100644
index 0000000000..cb9af5de66
--- /dev/null
+++ b/src/mscorlib/shared/System/ResolveEventHandler.cs
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Reflection;
+
+namespace System
+{
+ public delegate Assembly ResolveEventHandler(object sender, ResolveEventArgs args);
+}
diff --git a/src/mscorlib/shared/System/Resources/IResourceReader.cs b/src/mscorlib/shared/System/Resources/IResourceReader.cs
new file mode 100644
index 0000000000..543a5a67de
--- /dev/null
+++ b/src/mscorlib/shared/System/Resources/IResourceReader.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Abstraction to read streams of resources.
+**
+**
+===========================================================*/
+
+using System;
+using System.Collections;
+
+namespace System.Resources
+{
+ public interface IResourceReader : IEnumerable, IDisposable
+ {
+ // Interface does not need to be marked with the serializable attribute
+ // Closes the ResourceReader, releasing any resources associated with it.
+ // This could close a network connection, a file, or do nothing.
+ void Close();
+
+ new IDictionaryEnumerator GetEnumerator();
+ }
+}
diff --git a/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs b/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs
new file mode 100644
index 0000000000..70f41f4d9e
--- /dev/null
+++ b/src/mscorlib/shared/System/Resources/MissingManifestResourceException.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Resources
+{
+ [Serializable]
+ public class MissingManifestResourceException : SystemException
+ {
+ public MissingManifestResourceException()
+ : base(SR.Arg_MissingManifestResourceException)
+ {
+ HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE;
+ }
+
+ public MissingManifestResourceException(string message)
+ : base(message)
+ {
+ HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE;
+ }
+
+ public MissingManifestResourceException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = System.__HResults.COR_E_MISSINGMANIFESTRESOURCE;
+ }
+
+ protected MissingManifestResourceException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs b/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs
new file mode 100644
index 0000000000..b343e0cfbc
--- /dev/null
+++ b/src/mscorlib/shared/System/Resources/MissingSatelliteAssemblyException.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Exception for a missing satellite assembly needed
+** for ultimate resource fallback. This usually
+** indicates a setup and/or deployment problem.
+**
+**
+===========================================================*/
+
+using System;
+using System.Runtime.Serialization;
+
+namespace System.Resources
+{
+ [Serializable]
+ public class MissingSatelliteAssemblyException : SystemException
+ {
+ private String _cultureName;
+
+ public MissingSatelliteAssemblyException()
+ : base(SR.MissingSatelliteAssembly_Default)
+ {
+ HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY;
+ }
+
+ public MissingSatelliteAssemblyException(string message)
+ : base(message)
+ {
+ HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY;
+ }
+
+ public MissingSatelliteAssemblyException(string message, String cultureName)
+ : base(message)
+ {
+ HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY;
+ _cultureName = cultureName;
+ }
+
+ public MissingSatelliteAssemblyException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = System.__HResults.COR_E_MISSINGSATELLITEASSEMBLY;
+ }
+
+ protected MissingSatelliteAssemblyException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ public String CultureName
+ {
+ get { return _cultureName; }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Resources/NeutralResourcesLanguageAttribute.cs b/src/mscorlib/shared/System/Resources/NeutralResourcesLanguageAttribute.cs
new file mode 100644
index 0000000000..495c5205b2
--- /dev/null
+++ b/src/mscorlib/shared/System/Resources/NeutralResourcesLanguageAttribute.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Resources
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
+ public sealed class NeutralResourcesLanguageAttribute : Attribute
+ {
+ public NeutralResourcesLanguageAttribute(string cultureName)
+ {
+ if (cultureName == null)
+ throw new ArgumentNullException(nameof(cultureName));
+
+ CultureName = cultureName;
+ Location = UltimateResourceFallbackLocation.MainAssembly;
+ }
+
+ public NeutralResourcesLanguageAttribute(string cultureName, UltimateResourceFallbackLocation location)
+ {
+ if (cultureName == null)
+ throw new ArgumentNullException(nameof(cultureName));
+ if (!Enum.IsDefined(typeof(UltimateResourceFallbackLocation), location))
+ throw new ArgumentException(SR.Format(SR.Arg_InvalidNeutralResourcesLanguage_FallbackLoc, location));
+
+ CultureName = cultureName;
+ Location = location;
+ }
+
+ public string CultureName { get; }
+ public UltimateResourceFallbackLocation Location { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Resources/ResourceTypeCode.cs b/src/mscorlib/shared/System/Resources/ResourceTypeCode.cs
new file mode 100644
index 0000000000..b0ceb61df4
--- /dev/null
+++ b/src/mscorlib/shared/System/Resources/ResourceTypeCode.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Marker for types in .resources files
+**
+**
+===========================================================*/
+
+namespace System.Resources
+{
+ /* An internal implementation detail for .resources files, describing
+ what type an object is.
+ Ranges:
+ 0 - 0x1F Primitives and reserved values
+ 0x20 - 0x3F Specially recognized types, like byte[] and Streams
+
+ Note this data must be included in any documentation describing the
+ internals of .resources files.
+ */
+ internal enum ResourceTypeCode
+ {
+ // Primitives
+ Null = 0,
+ String = 1,
+ Boolean = 2,
+ Char = 3,
+ Byte = 4,
+ SByte = 5,
+ Int16 = 6,
+ UInt16 = 7,
+ Int32 = 8,
+ UInt32 = 9,
+ Int64 = 0xa,
+ UInt64 = 0xb,
+ Single = 0xc,
+ Double = 0xd,
+ Decimal = 0xe,
+ DateTime = 0xf,
+ TimeSpan = 0x10,
+
+ // A meta-value - change this if you add new primitives
+ LastPrimitive = TimeSpan,
+
+ // Types with a special representation, like byte[] and Stream
+ ByteArray = 0x20,
+ Stream = 0x21,
+
+ // User types - serialized using the binary formatter.
+ StartOfUserTypes = 0x40
+ }
+}
diff --git a/src/mscorlib/shared/System/Resources/SatelliteContractVersionAttribute.cs b/src/mscorlib/shared/System/Resources/SatelliteContractVersionAttribute.cs
new file mode 100644
index 0000000000..0707447677
--- /dev/null
+++ b/src/mscorlib/shared/System/Resources/SatelliteContractVersionAttribute.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+**
+** Purpose: Specifies which version of a satellite assembly
+** the ResourceManager should ask for.
+**
+**
+===========================================================*/
+
+namespace System.Resources
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
+ public sealed class SatelliteContractVersionAttribute : Attribute
+ {
+ public SatelliteContractVersionAttribute(String version)
+ {
+ if (version == null)
+ throw new ArgumentNullException(nameof(version));
+ Version = version;
+ }
+
+ public String Version { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Resources/UltimateResourceFallbackLocation.cs b/src/mscorlib/shared/System/Resources/UltimateResourceFallbackLocation.cs
new file mode 100644
index 0000000000..83640ec9fe
--- /dev/null
+++ b/src/mscorlib/shared/System/Resources/UltimateResourceFallbackLocation.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+**
+** Purpose: Tells the ResourceManager where to find the
+** ultimate fallback resources for your assembly.
+**
+**
+===========================================================*/
+
+using System;
+
+namespace System.Resources
+{
+ public enum UltimateResourceFallbackLocation
+ {
+ MainAssembly,
+ Satellite
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs
new file mode 100644
index 0000000000..25efcafa3f
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/AccessedThroughPropertyAttribute.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Field)]
+ public sealed class AccessedThroughPropertyAttribute : Attribute
+ {
+ public AccessedThroughPropertyAttribute(string propertyName)
+ {
+ PropertyName = propertyName;
+ }
+
+ public string PropertyName { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs
new file mode 100644
index 0000000000..198ed3d0e7
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/AsyncStateMachineAttribute.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ public sealed class AsyncStateMachineAttribute : StateMachineAttribute
+ {
+ public AsyncStateMachineAttribute(Type stateMachineType)
+ : base(stateMachineType)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/CallerFilePathAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/CallerFilePathAttribute.cs
new file mode 100644
index 0000000000..5858634b42
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/CallerFilePathAttribute.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+ public sealed class CallerFilePathAttribute : Attribute
+ {
+ public CallerFilePathAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs
new file mode 100644
index 0000000000..5bd2fcb91b
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/CallerLineNumberAttribute.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+ public sealed class CallerLineNumberAttribute : Attribute
+ {
+ public CallerLineNumberAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs
new file mode 100644
index 0000000000..8b046335b5
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/CallerMemberNameAttribute.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+ public sealed class CallerMemberNameAttribute : Attribute
+ {
+ public CallerMemberNameAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/CompilationRelaxations.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/CompilationRelaxations.cs
new file mode 100644
index 0000000000..4da95024c5
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/CompilationRelaxations.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ /// IMPORTANT: Keep this in sync with corhdr.h
+ [Flags]
+ [Serializable]
+ public enum CompilationRelaxations : int
+ {
+ NoStringInterning = 0x0008 // Start in 0x0008, we had other non public flags in this enum before,
+ // so we'll start here just in case somebody used them. This flag is only
+ // valid when set for Assemblies.
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/CompilationRelaxationsAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/CompilationRelaxationsAttribute.cs
new file mode 100644
index 0000000000..1f100bd415
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/CompilationRelaxationsAttribute.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Method)]
+ public class CompilationRelaxationsAttribute : Attribute
+ {
+ public CompilationRelaxationsAttribute(int relaxations)
+ {
+ CompilationRelaxations = relaxations;
+ }
+
+ public CompilationRelaxationsAttribute(CompilationRelaxations relaxations)
+ {
+ CompilationRelaxations = (int)relaxations;
+ }
+
+ public int CompilationRelaxations { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs
new file mode 100644
index 0000000000..3da2a95aeb
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/CompilerGeneratedAttribute.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.All, Inherited = true)]
+ public sealed class CompilerGeneratedAttribute : Attribute
+ {
+ public CompilerGeneratedAttribute() { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs
new file mode 100644
index 0000000000..22fa694200
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/CompilerGlobalScopeAttribute.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ // Attribute used to communicate to the VS7 debugger that a class should be treated as if it has global scope.
+
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Class)]
+ public class CompilerGlobalScopeAttribute : Attribute
+ {
+ public CompilerGlobalScopeAttribute() { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/DefaultDependencyAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/DefaultDependencyAttribute.cs
new file mode 100644
index 0000000000..f5419d413b
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/DefaultDependencyAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Assembly)]
+ public sealed class DefaultDependencyAttribute : Attribute
+ {
+ public DefaultDependencyAttribute(LoadHint loadHintArgument)
+ {
+ LoadHint = loadHintArgument;
+ }
+
+ public LoadHint LoadHint { get; }
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/DependencyAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/DependencyAttribute.cs
new file mode 100644
index 0000000000..56f4242bb1
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/DependencyAttribute.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class DependencyAttribute : Attribute
+ {
+ public DependencyAttribute(String dependentAssemblyArgument, LoadHint loadHintArgument)
+ {
+ DependentAssembly = dependentAssemblyArgument;
+ LoadHint = loadHintArgument;
+ }
+
+ public String DependentAssembly { get; }
+ public LoadHint LoadHint { get; }
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs
new file mode 100644
index 0000000000..4fc00e10ed
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/DisablePrivateReflectionAttribute.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
+ public sealed class DisablePrivateReflectionAttribute : Attribute
+ {
+ public DisablePrivateReflectionAttribute() { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/DiscardableAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/DiscardableAttribute.cs
new file mode 100644
index 0000000000..c88b3a7599
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/DiscardableAttribute.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ // Custom attribute to indicating a TypeDef is a discardable attribute.
+
+ public class DiscardableAttribute : Attribute
+ {
+ public DiscardableAttribute() { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ExtensionAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ExtensionAttribute.cs
new file mode 100644
index 0000000000..92170880f1
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ExtensionAttribute.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>
+ /// Indicates that a method is an extension method, or that a class or assembly contains extension methods.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
+ public sealed class ExtensionAttribute : Attribute { }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs
new file mode 100644
index 0000000000..baf5824241
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/FixedAddressValueTypeAttribute.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Field)]
+ public sealed class FixedAddressValueTypeAttribute : Attribute
+ {
+ public FixedAddressValueTypeAttribute() { }
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/FixedBufferAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/FixedBufferAttribute.cs
new file mode 100644
index 0000000000..bb8f00f686
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/FixedBufferAttribute.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+** Purpose: Used by a compiler for generating value types
+** in-place within other value types containing a certain
+** number of elements of the given (primitive) type. Somewhat
+** similar to P/Invoke's ByValTStr attribute.
+** Used by C# with this syntax: "fixed int buffer[10];"
+**
+===========================================================*/
+
+using System;
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Field, Inherited = false)]
+ public sealed class FixedBufferAttribute : Attribute
+ {
+ public FixedBufferAttribute(Type elementType, int length)
+ {
+ ElementType = elementType;
+ Length = length;
+ }
+
+ public Type ElementType { get; }
+ public int Length { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/FormattableStringFactory.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/FormattableStringFactory.cs
new file mode 100644
index 0000000000..23d03860ed
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/FormattableStringFactory.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: implementation of the FormattableStringFactory
+** class.
+**
+===========================================================*/
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>
+ /// A factory type used by compilers to create instances of the type <see cref="FormattableString"/>.
+ /// </summary>
+ public static class FormattableStringFactory
+ {
+ /// <summary>
+ /// Create a <see cref="FormattableString"/> from a composite format string and object
+ /// array containing zero or more objects to format.
+ /// </summary>
+ public static FormattableString Create(string format, params object[] arguments)
+ {
+ if (format == null)
+ {
+ throw new ArgumentNullException(nameof(format));
+ }
+
+ if (arguments == null)
+ {
+ throw new ArgumentNullException(nameof(arguments));
+ }
+
+ return new ConcreteFormattableString(format, arguments);
+ }
+
+ private sealed class ConcreteFormattableString : FormattableString
+ {
+ private readonly string _format;
+ private readonly object[] _arguments;
+
+ internal ConcreteFormattableString(string format, object[] arguments)
+ {
+ _format = format;
+ _arguments = arguments;
+ }
+
+ public override string Format { get { return _format; } }
+ public override object[] GetArguments() { return _arguments; }
+ public override int ArgumentCount { get { return _arguments.Length; } }
+ public override object GetArgument(int index) { return _arguments[index]; }
+ public override string ToString(IFormatProvider formatProvider) { return string.Format(formatProvider, _format, _arguments); }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/IAsyncStateMachine.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/IAsyncStateMachine.cs
new file mode 100644
index 0000000000..7fb7ea5395
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/IAsyncStateMachine.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// Represents state machines generated for asynchronous methods.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>
+ /// Represents state machines generated for asynchronous methods.
+ /// This type is intended for compiler use only.
+ /// </summary>
+ public interface IAsyncStateMachine
+ {
+ /// <summary>Moves the state machine to its next state.</summary>
+ void MoveNext();
+ /// <summary>Configures the state machine with a heap-allocated replica.</summary>
+ /// <param name="stateMachine">The heap-allocated replica.</param>
+ void SetStateMachine(IAsyncStateMachine stateMachine);
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/INotifyCompletion.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/INotifyCompletion.cs
new file mode 100644
index 0000000000..aba0a0691f
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/INotifyCompletion.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
+//
+//
+//
+// Interfaces used to represent instances that notify listeners of their completion via continuations.
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+using System;
+using System.Security;
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>
+ /// Represents an operation that will schedule continuations when the operation completes.
+ /// </summary>
+ public interface INotifyCompletion
+ {
+ /// <summary>Schedules the continuation action to be invoked when the instance completes.</summary>
+ /// <param name="continuation">The action to invoke when the operation completes.</param>
+ /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
+ void OnCompleted(Action continuation);
+ }
+
+ /// <summary>
+ /// Represents an awaiter used to schedule continuations when an await operation completes.
+ /// </summary>
+ public interface ICriticalNotifyCompletion : INotifyCompletion
+ {
+ /// <summary>Schedules the continuation action to be invoked when the instance completes.</summary>
+ /// <param name="continuation">The action to invoke when the operation completes.</param>
+ /// <exception cref="System.ArgumentNullException">The <paramref name="continuation"/> argument is null (Nothing in Visual Basic).</exception>
+ /// <remarks>Unlike OnCompleted, UnsafeOnCompleted need not propagate ExecutionContext information.</remarks>
+ void UnsafeOnCompleted(Action continuation);
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ITuple.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ITuple.cs
new file mode 100644
index 0000000000..cafee11f8a
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ITuple.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>
+ /// This interface is required for types that want to be indexed into by dynamic patterns.
+ /// </summary>
+ public interface ITuple
+ {
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int Length { get; }
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object this[int index] { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/IndexerNameAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/IndexerNameAttribute.cs
new file mode 100644
index 0000000000..65653a44d9
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/IndexerNameAttribute.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Property, Inherited = true)]
+ public sealed class IndexerNameAttribute : Attribute
+ {
+ public IndexerNameAttribute(String indexerName)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs
new file mode 100644
index 0000000000..f754694815
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/InternalsVisibleToAttribute.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
+ public sealed class InternalsVisibleToAttribute : Attribute
+ {
+ public InternalsVisibleToAttribute(string assemblyName)
+ {
+ AssemblyName = assemblyName;
+ }
+
+ public string AssemblyName { get; }
+ public bool AllInternalsVisible { get; set; } = true;
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/IsConst.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/IsConst.cs
new file mode 100644
index 0000000000..7f948b608a
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/IsConst.cs
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ public static partial class IsConst
+ {
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/IsVolatile.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/IsVolatile.cs
new file mode 100644
index 0000000000..fd1c6a1b12
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/IsVolatile.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ public static class IsVolatile
+ {
+ // no instantiation, please!
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs
new file mode 100644
index 0000000000..5ac3918028
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/IteratorStateMachineAttribute.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ public sealed class IteratorStateMachineAttribute : StateMachineAttribute
+ {
+ public IteratorStateMachineAttribute(Type stateMachineType)
+ : base(stateMachineType)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/LoadHint.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/LoadHint.cs
new file mode 100644
index 0000000000..ae6d9b9372
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/LoadHint.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ public enum LoadHint
+ {
+ Default = 0x0000, // No preference specified
+ Always = 0x0001, // Dependency is always loaded
+ Sometimes = 0x0002, // Dependency is sometimes loaded
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/MethodCodeType.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/MethodCodeType.cs
new file mode 100644
index 0000000000..e82993a5de
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/MethodCodeType.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Reflection;
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ public enum MethodCodeType
+ {
+ IL = MethodImplAttributes.IL,
+ Native = MethodImplAttributes.Native,
+ OPTIL = MethodImplAttributes.OPTIL,
+ Runtime = MethodImplAttributes.Runtime
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplOptions.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplOptions.cs
new file mode 100644
index 0000000000..2b5affc699
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/MethodImplOptions.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ // This Enum matchs the miImpl flags defined in corhdr.h. It is used to specify
+ // certain method properties.
+ [Flags]
+ public enum MethodImplOptions
+ {
+ Unmanaged = 0x0004,
+ NoInlining = 0x0008,
+ ForwardRef = 0x0010,
+ Synchronized = 0x0020,
+ NoOptimization = 0x0040,
+ PreserveSig = 0x0080,
+ AggressiveInlining = 0x0100,
+ InternalCall = 0x1000
+ }
+} \ No newline at end of file
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ReadOnlyAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ReadOnlyAttribute.cs
new file mode 100644
index 0000000000..aad7310412
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ReadOnlyAttribute.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.ComponentModel;
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>
+ /// Reserved to be used by the compiler for tracking metadata.
+ /// This attribute should not be used by developers in source code.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [AttributeUsage(AttributeTargets.All, Inherited = false)]
+ public sealed class ReadOnlyAttribute : Attribute
+ {
+ public ReadOnlyAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs
new file mode 100644
index 0000000000..6e307e72af
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ReferenceAssemblyAttribute.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+** Attribute: ReferenceAssemblyAttribute
+**
+** Purpose: Identifies an assembly as being a "reference
+** assembly", meaning it contains public surface area but
+** no usable implementation. Reference assemblies
+** should be loadable for introspection, but not execution.
+**
+============================================================*/
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
+ public sealed class ReferenceAssemblyAttribute : Attribute
+ {
+ public ReferenceAssemblyAttribute()
+ {
+ }
+
+ public ReferenceAssemblyAttribute(String description)
+ {
+ Description = description;
+ }
+
+ public String Description { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs
new file mode 100644
index 0000000000..55dba0d113
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeCompatibilityAttribute.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Mark up the program to indicate various legacy or new opt-in behaviors.
+**
+**
+=============================================================================*/
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
+ public sealed class RuntimeCompatibilityAttribute : Attribute
+ {
+ public RuntimeCompatibilityAttribute()
+ {
+ // legacy behavior is the default, and WrapNonExceptionThrows is implicitly
+ // false thanks to the CLR's guarantee of zeroed memory.
+ }
+
+ // If a non-CLSCompliant exception (i.e. one that doesn't derive from System.Exception) is
+ // thrown, should it be wrapped up in a System.Runtime.CompilerServices.RuntimeWrappedException
+ // instance when presented to catch handlers?
+ public bool WrapNonExceptionThrows { get; set; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs
new file mode 100644
index 0000000000..b93c435439
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/RuntimeFeature.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ public static class RuntimeFeature
+ {
+ /// <summary>
+ /// Checks whether a certain feature is supported by the Runtime.
+ /// </summary>
+ public static bool IsSupported(string feature)
+ {
+ // No features are supported for now.
+ // These features should be added as public static readonly string fields in the same class.
+ return false;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/SpecialNameAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/SpecialNameAttribute.cs
new file mode 100644
index 0000000000..b18e62895f
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/SpecialNameAttribute.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Struct)]
+ public sealed class SpecialNameAttribute : Attribute
+ {
+ public SpecialNameAttribute() { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/StateMachineAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/StateMachineAttribute.cs
new file mode 100644
index 0000000000..94ed5b5c74
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/StateMachineAttribute.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
+ public class StateMachineAttribute : Attribute
+ {
+ public StateMachineAttribute(Type stateMachineType)
+ {
+ StateMachineType = stateMachineType;
+ }
+
+ public Type StateMachineType { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/StringFreezingAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/StringFreezingAttribute.cs
new file mode 100644
index 0000000000..7772a1a263
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/StringFreezingAttribute.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ // Custom attribute to indicate that strings should be frozen.
+
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Assembly, Inherited = false)]
+ public sealed class StringFreezingAttribute : Attribute
+ {
+ public StringFreezingAttribute() { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/StrongBox.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/StrongBox.cs
new file mode 100644
index 0000000000..0a1a565f54
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/StrongBox.cs
@@ -0,0 +1,59 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>
+ /// Holds a reference to a value.
+ /// </summary>
+ /// <typeparam name="T">The type of the value that the <see cref = "StrongBox{T}"></see> references.</typeparam>
+ public class StrongBox<T> : IStrongBox
+ {
+ /// <summary>
+ /// Gets the strongly typed value associated with the <see cref = "StrongBox{T}"></see>
+ /// <remarks>This is explicitly exposed as a field instead of a property to enable loading the address of the field.</remarks>
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields")]
+ public T Value;
+
+ /// <summary>
+ /// Initializes a new StrongBox which can receive a value when used in a reference call.
+ /// </summary>
+ public StrongBox()
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref = "StrongBox{T}"></see> with the specified value.
+ /// </summary>
+ /// <param name="value">A value that the <see cref = "StrongBox{T}"></see> will reference.</param>
+ public StrongBox(T value)
+ {
+ Value = value;
+ }
+
+ object IStrongBox.Value
+ {
+ get
+ {
+ return Value;
+ }
+ set
+ {
+ Value = (T)value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Defines a property for accessing the value that an object references.
+ /// </summary>
+ public interface IStrongBox
+ {
+ /// <summary>
+ /// Gets or sets the value the object references.
+ /// </summary>
+ object Value { get; set; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs
new file mode 100644
index 0000000000..b4224b1c89
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/SuppressIldasmAttribute.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module)]
+ public sealed class SuppressIldasmAttribute : Attribute
+ {
+ public SuppressIldasmAttribute() { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/TupleElementNamesAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/TupleElementNamesAttribute.cs
new file mode 100644
index 0000000000..ad923dfae5
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/TupleElementNamesAttribute.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+
+namespace System.Runtime.CompilerServices
+{
+ /// <summary>
+ /// Indicates that the use of <see cref="System.ValueTuple"/> on a member is meant to be treated as a tuple with element names.
+ /// </summary>
+ [CLSCompliant(false)]
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Event)]
+ public sealed class TupleElementNamesAttribute : Attribute
+ {
+ private readonly string[] _transformNames;
+
+ /// <summary>
+ /// Initializes a new instance of the <see
+ /// cref="TupleElementNamesAttribute"/> class.
+ /// </summary>
+ /// <param name="transformNames">
+ /// Specifies, in a pre-order depth-first traversal of a type's
+ /// construction, which <see cref="System.ValueType"/> occurrences are
+ /// meant to carry element names.
+ /// </param>
+ /// <remarks>
+ /// This constructor is meant to be used on types that contain an
+ /// instantiation of <see cref="System.ValueType"/> that contains
+ /// element names. For instance, if <c>C</c> is a generic type with
+ /// two type parameters, then a use of the constructed type <c>C{<see
+ /// cref="System.ValueTuple{T1, T2}"/>, <see
+ /// cref="System.ValueTuple{T1, T2, T3}"/></c> might be intended to
+ /// treat the first type argument as a tuple with element names and the
+ /// second as a tuple without element names. In which case, the
+ /// appropriate attribute specification should use a
+ /// <c>transformNames</c> value of <c>{ "name1", "name2", null, null,
+ /// null }</c>.
+ /// </remarks>
+ public TupleElementNamesAttribute(string[] transformNames)
+ {
+ if (transformNames == null)
+ {
+ throw new ArgumentNullException(nameof(transformNames));
+ }
+
+ _transformNames = transformNames;
+ }
+
+ /// <summary>
+ /// Specifies, in a pre-order depth-first traversal of a type's
+ /// construction, which <see cref="System.ValueTuple"/> elements are
+ /// meant to carry element names.
+ /// </summary>
+ public IList<string> TransformNames => _transformNames;
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs
new file mode 100644
index 0000000000..c4a8558243
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/TypeForwardedFromAttribute.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)]
+ public sealed class TypeForwardedFromAttribute : Attribute
+ {
+ public TypeForwardedFromAttribute(string assemblyFullName)
+ {
+ AssemblyFullName = assemblyFullName;
+ }
+
+ public string AssemblyFullName { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs
new file mode 100644
index 0000000000..85d5c030c1
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/TypeForwardedToAttribute.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]
+ public sealed class TypeForwardedToAttribute : Attribute
+ {
+ public TypeForwardedToAttribute(Type destination)
+ {
+ Destination = destination;
+ }
+
+ public Type Destination { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs
new file mode 100644
index 0000000000..162676efe8
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.CompilerServices
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Struct)]
+ sealed public class UnsafeValueTypeAttribute : Attribute
+ {
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/ConstrainedExecution/Cer.cs b/src/mscorlib/shared/System/Runtime/ConstrainedExecution/Cer.cs
new file mode 100644
index 0000000000..c142ec9ecc
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/ConstrainedExecution/Cer.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.ConstrainedExecution
+{
+ [Serializable]
+ public enum Cer : int
+ {
+ None = 0,
+ MayFail = 1, // Might fail, but the method will say it failed
+ Success = 2,
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/ConstrainedExecution/Consistency.cs b/src/mscorlib/shared/System/Runtime/ConstrainedExecution/Consistency.cs
new file mode 100644
index 0000000000..7ee8480e89
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/ConstrainedExecution/Consistency.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.ConstrainedExecution
+{
+ [Serializable]
+ public enum Consistency : int
+ {
+ MayCorruptProcess = 0,
+ MayCorruptAppDomain = 1,
+ MayCorruptInstance = 2,
+ WillNotCorruptState = 3,
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/ConstrainedExecution/ReliabilityContractAttribute.cs b/src/mscorlib/shared/System/Runtime/ConstrainedExecution/ReliabilityContractAttribute.cs
new file mode 100644
index 0000000000..b3cb0143fa
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/ConstrainedExecution/ReliabilityContractAttribute.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+/*============================================================
+**
+**
+**
+** Purpose: Defines a publically documentable contract for
+** reliability between a method and its callers, expressing
+** what state will remain consistent in the presence of
+** failures (ie async exceptions like thread abort) and whether
+** the method needs to be called from within a CER.
+**
+**
+===========================================================*/
+
+namespace System.Runtime.ConstrainedExecution
+{
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Interface /* | AttributeTargets.Delegate*/, Inherited = false)]
+ public sealed class ReliabilityContractAttribute : Attribute
+ {
+ public ReliabilityContractAttribute(Consistency consistencyGuarantee, Cer cer)
+ {
+ ConsistencyGuarantee = consistencyGuarantee;
+ Cer = cer;
+ }
+
+ public Consistency ConsistencyGuarantee { get; }
+ public Cer Cer { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/CallingConvention.cs b/src/mscorlib/shared/System/Runtime/InteropServices/CallingConvention.cs
new file mode 100644
index 0000000000..3b18fdee3a
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/CallingConvention.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.InteropServices
+{
+ // Used for the CallingConvention named argument to the DllImport and NativeCallable attribute
+ public enum CallingConvention
+ {
+ Winapi = 1,
+ Cdecl = 2,
+ StdCall = 3,
+ ThisCall = 4,
+ FastCall = 5,
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/CharSet.cs b/src/mscorlib/shared/System/Runtime/InteropServices/CharSet.cs
new file mode 100644
index 0000000000..d587ec006b
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/CharSet.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.InteropServices
+{
+ // Use this in P/Invoke function prototypes to specify
+ // which character set to use when marshalling Strings.
+ // Using Ansi will marshal the strings as 1 byte char*'s.
+ // Using Unicode will marshal the strings as 2 byte wchar*'s.
+ // Generally you probably want to use Auto, which does the
+ // right thing 99% of the time.
+
+ public enum CharSet
+ {
+ None = 1, // User didn't specify how to marshal strings.
+ Ansi = 2, // Strings should be marshalled as ANSI 1 byte chars.
+ Unicode = 3, // Strings should be marshalled as Unicode 2 byte chars.
+ Auto = 4, // Marshal Strings in the right way for the target system.
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/ComVisibleAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/ComVisibleAttribute.cs
new file mode 100644
index 0000000000..84b9505a5a
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/ComVisibleAttribute.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, Inherited = false)]
+ public sealed class ComVisibleAttribute : Attribute
+ {
+ public ComVisibleAttribute(bool visibility)
+ {
+ Value = visibility;
+ }
+
+ public bool Value { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs b/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs
new file mode 100644
index 0000000000..d7bde79c43
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/ExternalException.cs
@@ -0,0 +1,89 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception base class for all errors from Interop or Structured
+** Exception Handling code.
+**
+**
+=============================================================================*/
+
+using System;
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace System.Runtime.InteropServices
+{
+ // Base exception for COM Interop errors &; Structured Exception Handler
+ // exceptions.
+ //
+ [Serializable]
+ public class ExternalException : SystemException
+ {
+ public ExternalException()
+ : base(SR.Arg_ExternalException)
+ {
+ HResult = __HResults.E_FAIL;
+ }
+
+ public ExternalException(string message)
+ : base(message)
+ {
+ HResult = __HResults.E_FAIL;
+ }
+
+ public ExternalException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.E_FAIL;
+ }
+
+ public ExternalException(string message, int errorCode)
+ : base(message)
+ {
+ HResult = errorCode;
+ }
+
+ protected ExternalException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ public virtual int ErrorCode
+ {
+ get
+ {
+ return HResult;
+ }
+ }
+
+ public override string ToString()
+ {
+ string message = Message;
+ string className = GetType().ToString();
+
+ string s = className + " (0x" + HResult.ToString("X8", CultureInfo.InvariantCulture) + ")";
+
+ if (!(String.IsNullOrEmpty(message)))
+ {
+ s = s + ": " + message;
+ }
+
+ Exception innerException = InnerException;
+
+ if (innerException != null)
+ {
+ s = s + " ---> " + innerException.ToString();
+ }
+
+ if (StackTrace != null)
+ s += Environment.NewLine + StackTrace;
+
+ return s;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/LayoutKind.cs b/src/mscorlib/shared/System/Runtime/InteropServices/LayoutKind.cs
new file mode 100644
index 0000000000..dbd7ec62d5
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/LayoutKind.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.InteropServices
+{
+ // Used in the StructLayoutAttribute class
+ public enum LayoutKind
+ {
+ Sequential = 0,
+ Explicit = 2,
+ Auto = 3,
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/StringBuffer.cs b/src/mscorlib/shared/System/Runtime/InteropServices/StringBuffer.cs
new file mode 100644
index 0000000000..fdd0b95590
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/StringBuffer.cs
@@ -0,0 +1,301 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Buffers;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices
+{
+ /// <summary>
+ /// Buffer that deals in char size increments. Dispose to free memory. Always makes ordinal
+ /// comparisons. Not thread safe.
+ ///
+ /// A more performant replacement for StringBuilder when performing native interop.
+ ///
+ /// "No copy" valuetype. Has to be passed as "ref".
+ ///
+ /// </summary>
+ /// <remarks>
+ /// Suggested use through P/Invoke: define DllImport arguments that take a character buffer as SafeHandle and pass StringBuffer.GetHandle().
+ /// </remarks>
+ internal struct StringBuffer
+ {
+ private char[] _buffer;
+ private int _length;
+
+ /// <summary>
+ /// Instantiate the buffer with capacity for at least the specified number of characters. Capacity
+ /// includes the trailing null character.
+ /// </summary>
+ public StringBuffer(int initialCapacity)
+ {
+ _buffer = ArrayPool<char>.Shared.Rent(initialCapacity);
+ _length = 0;
+ }
+
+ /// <summary>
+ /// Get/set the character at the given index.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">Thrown if attempting to index outside of the buffer length.</exception>
+ public char this[int index]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index));
+ return _buffer[index];
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ if (index >= _length) throw new ArgumentOutOfRangeException(nameof(index));
+ _buffer[index] = value;
+ }
+ }
+
+ /// <summary>
+ /// Underlying storage of the buffer. Used for interop.
+ /// </summary>
+ public char[] UnderlyingArray => _buffer;
+
+ /// <summary>
+ /// Character capacity of the buffer. Includes the count for the trailing null character.
+ /// </summary>
+ public int Capacity => _buffer.Length;
+
+ /// <summary>
+ /// Ensure capacity in characters is at least the given minimum.
+ /// </summary>
+ /// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception>
+ public void EnsureCapacity(int minCapacity)
+ {
+ if (minCapacity > Capacity)
+ {
+ char[] oldBuffer = _buffer;
+ _buffer = ArrayPool<char>.Shared.Rent(minCapacity);
+ Array.Copy(oldBuffer, 0, _buffer, 0, oldBuffer.Length);
+ ArrayPool<char>.Shared.Return(oldBuffer);
+ }
+ }
+
+ /// <summary>
+ /// The logical length of the buffer in characters. (Does not include the final null.) Will automatically attempt to increase capacity.
+ /// This is where the usable data ends.
+ /// </summary>
+ /// <exception cref="OutOfMemoryException">Thrown if unable to allocate memory when setting.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">Thrown if the set size in bytes is int.MaxValue (as space is implicitly reserved for the trailing null).</exception>
+ public int Length
+ {
+ get { return _length; }
+ set
+ {
+ // Null terminate
+ EnsureCapacity(checked(value + 1));
+ _buffer[value] = '\0';
+
+ _length = value;
+ }
+ }
+
+ /// <summary>
+ /// True if the buffer contains the given character.
+ /// </summary>
+ public unsafe bool Contains(char value)
+ {
+ fixed (char* start = _buffer)
+ {
+ int length = _length;
+ for (int i = 0; i < length; i++)
+ {
+ if (start[i] == value) return true;
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Returns true if the buffer starts with the given string.
+ /// </summary>
+ public bool StartsWith(string value)
+ {
+ if (value == null) throw new ArgumentNullException(nameof(value));
+ if (_length < value.Length) return false;
+ return SubstringEquals(value, startIndex: 0, count: value.Length);
+ }
+
+ /// <summary>
+ /// Returns true if the specified StringBuffer substring equals the given value.
+ /// </summary>
+ /// <param name="value">The value to compare against the specified substring.</param>
+ /// <param name="startIndex">Start index of the sub string.</param>
+ /// <param name="count">Length of the substring, or -1 to check all remaining.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
+ /// of the buffer's length.
+ /// </exception>
+ public unsafe bool SubstringEquals(string value, int startIndex = 0, int count = -1)
+ {
+ if (value == null) return false;
+ if (count < -1) throw new ArgumentOutOfRangeException(nameof(count));
+ if (startIndex > _length) throw new ArgumentOutOfRangeException(nameof(startIndex));
+
+ int realCount = count == -1 ? _length - startIndex : (int)count;
+ if (checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException(nameof(count));
+
+ int length = value.Length;
+
+ // Check the substring length against the input length
+ if (realCount != length) return false;
+
+ fixed (char* valueStart = value)
+ fixed (char* bufferStart = _buffer)
+ {
+ char* subStringStart = bufferStart + startIndex;
+
+ for (int i = 0; i < length; i++)
+ {
+ if (subStringStart[i] != valueStart[i]) return false;
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Append the given buffer.
+ /// </summary>
+ /// <param name="value">The buffer to append.</param>
+ /// <param name="startIndex">The index in the input buffer to start appending from.</param>
+ /// <param name="count">The count of characters to copy from the buffer string.</param>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
+ /// of <paramref name="value"/> characters.
+ /// </exception>
+ public void Append(ref StringBuffer value, int startIndex = 0)
+ {
+ if (value.Length == 0) return;
+
+ value.CopyTo(
+ bufferIndex: startIndex,
+ destination: ref this,
+ destinationIndex: _length,
+ count: value.Length);
+ }
+
+ /// <summary>
+ /// Append the given buffer.
+ /// </summary>
+ /// <param name="value">The buffer to append.</param>
+ /// <param name="startIndex">The index in the input buffer to start appending from.</param>
+ /// <param name="count">The count of characters to copy from the buffer string.</param>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range
+ /// of <paramref name="value"/> characters.
+ /// </exception>
+ public void Append(ref StringBuffer value, int startIndex, int count)
+ {
+ if (count == 0) return;
+
+ value.CopyTo(
+ bufferIndex: startIndex,
+ destination: ref this,
+ destinationIndex: _length,
+ count: count);
+ }
+
+ /// <summary>
+ /// Copy contents to the specified buffer. Destination index must be within current destination length.
+ /// Will grow the destination buffer if needed.
+ /// </summary>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// Thrown if <paramref name="bufferIndex"/> or <paramref name="destinationIndex"/> or <paramref name="count"/> are outside the range
+ /// of <paramref name="value"/> characters.
+ /// </exception>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="destination"/> is null.</exception>
+ public void CopyTo(int bufferIndex, ref StringBuffer destination, int destinationIndex, int count)
+ {
+ if (destinationIndex > destination._length) throw new ArgumentOutOfRangeException(nameof(destinationIndex));
+ if (bufferIndex >= _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex));
+ if (_length < checked(bufferIndex + count)) throw new ArgumentOutOfRangeException(nameof(count));
+
+ if (count == 0) return;
+ int lastIndex = checked(destinationIndex + count);
+ if (destination.Length < lastIndex) destination.Length = lastIndex;
+
+ Array.Copy(UnderlyingArray, bufferIndex, destination.UnderlyingArray, destinationIndex, count);
+ }
+
+ /// <summary>
+ /// Copy contents from the specified string into the buffer at the given index. Start index must be within the current length of
+ /// the buffer, will grow as necessary.
+ /// </summary>
+ public void CopyFrom(int bufferIndex, string source, int sourceIndex = 0, int count = -1)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+ if (bufferIndex > _length) throw new ArgumentOutOfRangeException(nameof(bufferIndex));
+ if (sourceIndex < 0 || sourceIndex > source.Length) throw new ArgumentOutOfRangeException(nameof(sourceIndex));
+ if (count == -1) count = source.Length - sourceIndex;
+ if (count < 0 || source.Length - count < sourceIndex) throw new ArgumentOutOfRangeException(nameof(count));
+
+ if (count == 0) return;
+ int lastIndex = bufferIndex + (int)count;
+ if (_length < lastIndex) Length = lastIndex;
+
+ source.CopyTo(sourceIndex, UnderlyingArray, bufferIndex, count);
+ }
+
+ /// <summary>
+ /// Trim the specified values from the end of the buffer. If nothing is specified, nothing is trimmed.
+ /// </summary>
+ public void TrimEnd(char[] values)
+ {
+ if (values == null || values.Length == 0 || _length == 0) return;
+
+ while (_length > 0 && Array.IndexOf(values, _buffer[_length - 1]) >= 0)
+ {
+ Length = _length - 1;
+ }
+ }
+
+ /// <summary>
+ /// String representation of the entire buffer. If the buffer is larger than the maximum size string (int.MaxValue) this will throw.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">Thrown if the buffer is too big to fit into a string.</exception>
+ public override string ToString()
+ {
+ return new string(_buffer, startIndex: 0, length: _length);
+ }
+
+ /// <summary>
+ /// Get the given substring in the buffer.
+ /// </summary>
+ /// <param name="count">Count of characters to take, or remaining characters from <paramref name="startIndex"/> if -1.</param>
+ /// <exception cref="ArgumentOutOfRangeException">
+ /// Thrown if <paramref name="startIndex"/> or <paramref name="count"/> are outside the range of the buffer's length
+ /// or count is greater than the maximum string size (int.MaxValue).
+ /// </exception>
+ public string Substring(int startIndex, int count = -1)
+ {
+ if (startIndex > (_length == 0 ? 0 : _length - 1)) throw new ArgumentOutOfRangeException(nameof(startIndex));
+ if (count < -1) throw new ArgumentOutOfRangeException(nameof(count));
+
+ int realCount = count == -1 ? _length - startIndex : (int)count;
+ if (realCount > int.MaxValue || checked(startIndex + realCount) > _length) throw new ArgumentOutOfRangeException(nameof(count));
+
+ // The buffer could be bigger than will fit into a string, but the substring might fit. As the starting
+ // index might be bigger than int we need to index ourselves.
+ return new string(_buffer, startIndex: startIndex, length: realCount);
+ }
+
+ public void Free()
+ {
+ ArrayPool<char>.Shared.Return(_buffer);
+ _buffer = null;
+ _length = 0;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs b/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs
new file mode 100644
index 0000000000..2d69c95afe
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedFunctionPointerAttribute.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.InteropServices
+{
+ [AttributeUsage(AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
+ public sealed class UnmanagedFunctionPointerAttribute : Attribute
+ {
+ public bool BestFitMapping;
+ public bool SetLastError;
+ public bool ThrowOnUnmappableChar;
+ public CharSet CharSet;
+
+ public UnmanagedFunctionPointerAttribute()
+ {
+ CallingConvention = CallingConvention.Winapi;
+ }
+
+ public UnmanagedFunctionPointerAttribute(CallingConvention callingConvention)
+ {
+ CallingConvention = callingConvention;
+ }
+
+ public CallingConvention CallingConvention { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedType.cs b/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedType.cs
new file mode 100644
index 0000000000..4deca7fe0c
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/UnmanagedType.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.InteropServices
+{
+ public enum UnmanagedType
+ {
+ Bool = 0x2, // 4 byte boolean value (true != 0, false == 0)
+ I1 = 0x3, // 1 byte signed value
+ U1 = 0x4, // 1 byte unsigned value
+ I2 = 0x5, // 2 byte signed value
+ U2 = 0x6, // 2 byte unsigned value
+ I4 = 0x7, // 4 byte signed value
+ U4 = 0x8, // 4 byte unsigned value
+ I8 = 0x9, // 8 byte signed value
+ U8 = 0xa, // 8 byte unsigned value
+ R4 = 0xb, // 4 byte floating point
+ R8 = 0xc, // 8 byte floating point
+ Currency = 0xf, // A currency
+ BStr = 0x13, // OLE Unicode BSTR
+ LPStr = 0x14, // Ptr to SBCS string
+ LPWStr = 0x15, // Ptr to Unicode string
+ LPTStr = 0x16, // Ptr to OS preferred (SBCS/Unicode) string
+ ByValTStr = 0x17, // OS preferred (SBCS/Unicode) inline string (only valid in structs)
+ IUnknown = 0x19, // COM IUnknown pointer.
+ IDispatch = 0x1a, // COM IDispatch pointer
+ Struct = 0x1b, // Structure
+ Interface = 0x1c, // COM interface
+ SafeArray = 0x1d, // OLE SafeArray
+ ByValArray = 0x1e, // Array of fixed size (only valid in structs)
+ SysInt = 0x1f, // Hardware natural sized signed integer
+ SysUInt = 0x20,
+ VBByRefStr = 0x22,
+ AnsiBStr = 0x23, // OLE BSTR containing SBCS characters
+ TBStr = 0x24, // Ptr to OS preferred (SBCS/Unicode) BSTR
+ VariantBool = 0x25, // OLE defined BOOLEAN (2 bytes, true == -1, false == 0)
+ FunctionPtr = 0x26, // Function pointer
+ AsAny = 0x28, // Paired with Object type and does runtime marshalling determination
+ LPArray = 0x2a, // C style array
+ LPStruct = 0x2b, // Pointer to a structure
+ CustomMarshaler = 0x2c,
+ Error = 0x2d,
+ IInspectable = 0x2e,
+ HString = 0x2f, // Windows Runtime HSTRING
+ LPUTF8Str = 0x30, // UTF8 string
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/InteropServices/VarEnum.cs b/src/mscorlib/shared/System/Runtime/InteropServices/VarEnum.cs
new file mode 100644
index 0000000000..495aeca6d1
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/InteropServices/VarEnum.cs
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.InteropServices
+{
+ public enum VarEnum
+ {
+ VT_EMPTY = 0,
+ VT_NULL = 1,
+ VT_I2 = 2,
+ VT_I4 = 3,
+ VT_R4 = 4,
+ VT_R8 = 5,
+ VT_CY = 6,
+ VT_DATE = 7,
+ VT_BSTR = 8,
+ VT_DISPATCH = 9,
+ VT_ERROR = 10,
+ VT_BOOL = 11,
+ VT_VARIANT = 12,
+ VT_UNKNOWN = 13,
+ VT_DECIMAL = 14,
+ VT_I1 = 16,
+ VT_UI1 = 17,
+ VT_UI2 = 18,
+ VT_UI4 = 19,
+ VT_I8 = 20,
+ VT_UI8 = 21,
+ VT_INT = 22,
+ VT_UINT = 23,
+ VT_VOID = 24,
+ VT_HRESULT = 25,
+ VT_PTR = 26,
+ VT_SAFEARRAY = 27,
+ VT_CARRAY = 28,
+ VT_USERDEFINED = 29,
+ VT_LPSTR = 30,
+ VT_LPWSTR = 31,
+ VT_RECORD = 36,
+ VT_FILETIME = 64,
+ VT_BLOB = 65,
+ VT_STREAM = 66,
+ VT_STORAGE = 67,
+ VT_STREAMED_OBJECT = 68,
+ VT_STORED_OBJECT = 69,
+ VT_BLOB_OBJECT = 70,
+ VT_CF = 71,
+ VT_CLSID = 72,
+ VT_VECTOR = 0x1000,
+ VT_ARRAY = 0x2000,
+ VT_BYREF = 0x4000
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/IDeserializationCallback.cs b/src/mscorlib/shared/System/Runtime/Serialization/IDeserializationCallback.cs
new file mode 100644
index 0000000000..a1c1671a8b
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/IDeserializationCallback.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ public interface IDeserializationCallback
+ {
+ void OnDeserialization(object sender);
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/IFormatterConverter.cs b/src/mscorlib/shared/System/Runtime/Serialization/IFormatterConverter.cs
new file mode 100644
index 0000000000..c173144854
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/IFormatterConverter.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ [CLSCompliant(false)]
+ public interface IFormatterConverter
+ {
+ object Convert(object value, Type type);
+ object Convert(object value, TypeCode typeCode);
+ bool ToBoolean(object value);
+ char ToChar(object value);
+ sbyte ToSByte(object value);
+ byte ToByte(object value);
+ short ToInt16(object value);
+ ushort ToUInt16(object value);
+ int ToInt32(object value);
+ uint ToUInt32(object value);
+ long ToInt64(object value);
+ ulong ToUInt64(object value);
+ float ToSingle(object value);
+ double ToDouble(object value);
+ decimal ToDecimal(object value);
+ DateTime ToDateTime(object value);
+ string ToString(object value);
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/IObjectReference.cs b/src/mscorlib/shared/System/Runtime/Serialization/IObjectReference.cs
new file mode 100644
index 0000000000..d41bc50dde
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/IObjectReference.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ public interface IObjectReference
+ {
+ object GetRealObject(StreamingContext context);
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/ISafeSerializationData.cs b/src/mscorlib/shared/System/Runtime/Serialization/ISafeSerializationData.cs
new file mode 100644
index 0000000000..5089d134c3
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/ISafeSerializationData.cs
@@ -0,0 +1,207 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ //
+ // #SafeSerialization
+ //
+ // Types which are serializable via the ISerializable interface have a problem when it comes to allowing
+ // transparent subtypes which can allow themselves to serialize since the GetObjectData method is
+ // SecurityCritical.
+ //
+ // For instance, System.Exception implements ISerializable, however it is also desirable to have
+ // transparent exceptions with their own fields that need to be serialized. (For instance, in transparent
+ // assemblies such as the DLR and F#, or even in partial trust application code). Since overriding
+ // GetObjectData requires that the overriding method be security critical, this won't work directly.
+ //
+ // SafeSerializationManager solves this problem by allowing any partial trust code to contribute
+ // individual chunks of serializable data to be included in the serialized version of the derived class.
+ // These chunks are then deserialized back out of the serialized type and notified that they should
+ // populate the fields of the deserialized object when serialization is complete. This allows partial
+ // trust or transparent code to participate in serialization of an ISerializable type without having to
+ // override GetObjectData or implement the ISerializable constructor.
+ //
+ // On the serialization side, SafeSerializationManager has an event SerializeObjectState which it will
+ // fire in response to serialization in order to gather the units of serializable data that should be
+ // stored with the rest of the object during serialization. Methods which respond to these events
+ // create serializable objects which implement the ISafeSerializationData interface and add them to the
+ // collection of other serialized data by calling AddSerializedState on the SafeSerializationEventArgs
+ // passed into the event.
+ //
+ // By using an event rather than a virtual method on the base ISerializable object, we allow multiple
+ // potentially untrusted subclasses to participate in serialization, without each one having to ensure
+ // that it calls up to the base type in order for the whole system to work. (For instance Exception :
+ // TrustedException : UntrustedException, in this scenario UntrustedException would be able to override
+ // the virtual method an prevent TrustedException from ever seeing the method call, either accidentally
+ // or maliciously).
+ //
+ // Further, by only allowing additions of new chunks of serialization state rather than exposing the
+ // whole underlying list, we avoid exposing potentially sensitive serialized state to any of the
+ // potentially untrusted subclasses.
+ //
+ // At deserialization time, SafeSerializationManager performs the reverse operation. It deserializes the
+ // chunks of serialized state, and then notifies them that the object they belong to is deserialized by
+ // calling their CompleteSerialization method. In repsonse to this call, the state objects populate the
+ // fields of the object being deserialized with the state that they held.
+ //
+ // From a security perspective, the chunks of serialized state can only contain data that the specific
+ // subclass itself had access to read (otherwise it wouldn't be able to populate the type with that
+ // data), as opposed to having access to far more data in the SerializationInfo that GetObjectData uses.
+ // Similarly, at deserialization time, the serialized state can only modify fields that the type itself
+ // has access to (again, as opposed to the full SerializationInfo which could be modified).
+ //
+ // Individual types which wish to participate in safe serialization do so by containing an instance of a
+ // SafeSerializationManager and exposing its serialization event. During GetObjectData, the
+ // SafeSerializationManager is serialized just like any other field of the containing type. However, at
+ // the end of serialization it is called back one last time to CompleteSerialization.
+ //
+ // In CompleteSerialization, if the SafeSerializationManager detects that it has extra chunks of
+ // data to handle, it substitutes the root type being serialized (formerly the real type hosting the
+ // SafeSerializationManager) with itself. This allows it to gain more control over the deserialization
+ // process. It also saves away an extra bit of state in the serialization info indicating the real type
+ // of object that should be recreated during deserialization.
+ //
+ // At this point the serialized state looks like this:
+ // Data:
+ // realSerializedData1
+ // ...
+ // realSerializedDataN
+ // safeSerializationData -> this is the serialization data member of the parent type
+ // _serializedState -> list of saved serialized states from subclasses responding to the safe
+ // serialization event
+ // RealTypeSerializationName -> type which is using safe serialization
+ // Type:
+ // SafeSerializationManager
+ //
+ // That is, the serialized data claims to be of type SafeSerializationManager, however contains only the
+ // data from the real object being serialized along with one bit of safe serialization metadata.
+ //
+ // At deserialization time, since the serialized data claims to be of type SafeSerializationManager, the
+ // root object being created is an instance of the SafeSerializationManager class. However, it detects
+ // that this isn't a real SafeSerializationManager (by looking for the real type field in the metadata),
+ // and simply saves away the SerializationInfo and the real type being deserialized.
+ //
+ // Since SafeSerializationManager implements IObjectReference, the next step of deserialization is the
+ // GetRealObject callback. This callback is the one responsible for getting the
+ // SafeSerializationManager out of the way and instead creating an instance of the actual type which was
+ // serialized.
+ //
+ // It does this by first creating an instance of the real type being deserialzed (saved away in the
+ // deserialzation constructor), but not running any of its constructors. Instead, it walks the
+ // inheritance hierarchy (moving toward the most derived type) looking for the last full trust type to
+ // implement the standard ISerializable constructor before any type does not implement the constructor.
+ // It is this last type's deserialization constructor which is then invoked, passing in the saved
+ // SerializationInfo. Once the constructors are run, we return this object as the real deserialized
+ // object.
+ //
+ // The reason that we do this walk is so that ISerializable types can protect themselves from malicious
+ // input during deserialization by making their deserialization constructors unavailable to partial
+ // trust code. By not requiring every type have a copy of this constructor, partial trust code can
+ // participate in safe serialization and not be required to have access to the parent's constructor.
+ //
+ // It should be noted however, that this heuristic means that if a full trust type does derive from
+ // a transparent or partial trust type using this safe serialization mechanism, that full trust type
+ // will not have its constructor called. Further, the protection of not invoking partial trust
+ // deserialization constructors only comes into play if SafeSerializationManager is in control of
+ // deserialization, which means there must be at least one (even empty) safe serialization event
+ // handler registered.
+ //
+ // Another interesting note is that at this point there are now two SafeSerializationManagers alive for
+ // this deserialization. The first object is the one which is controlling the deserialization and was
+ // created as the root object of the deserialization. The second one is the object which contains the
+ // serialized data chunks and is a data member of the real object being deserialized. For this reason,
+ // the data objects cannot be notified that the deserialization is complete during GetRealObject since
+ // the ISafeSerializationData objects are not members of the active SafeSerializationManager instance.
+ //
+ // The next step is the OnDeserialized callback, which comes to SafeSerializableObject since it was
+ // pretending to be the root object of the deserialization. It responds to this callback by calling
+ // any existing OnDeserialized callback on the real type that was deserialized.
+ //
+ // The real type needs to call its data member SafeSerializationData object's CompleteDeserialization
+ // method in response to the OnDeserialized call. This CompleteDeserialization call will then iterate
+ // through the ISafeSerializationData objects calling each of their CompleteDeserialization methods so
+ // that they can plug the nearly-complete object with their saved data.
+ //
+ // The reason for having a new ISafeSerializationData interface which is basically identical to
+ // IDeserializationCallback is that IDeserializationCallback will be called on the stored data chunks
+ // by the serialization code when they are deserialized, and that's not a desirable behavior.
+ // Essentially, we need to change the meaning of the object parameter to mean "parent object which
+ // participated in safe serialization", rather than "this object".
+ //
+ // Implementing safe serialization on an ISerialiable type is relatively straight forward. (For an
+ // example, see System.Exception):
+ //
+ // 1. Include a data member of type SafeSerializationManager:
+ //
+ // private SafeSerializationManager _safeSerializationManager;
+ //
+ // 2. Add a protected SerializeObjectState event, which passes through to the SafeSerializationManager:
+ //
+ // protected event EventHandler<SafeSerializationEventArgs> SerializeObjectState
+ // {
+ // add { _safeSerializationManager.SerializeObjectState += value; }
+ // remove { _safeSerializationManager.SerializeObjectState -= value; }
+ // }
+ //
+ // 3. Serialize the safe serialization object in GetObjectData, and call its CompleteSerialization method:
+ //
+ // {
+ // info.AddValue("_safeSerializationManager", _safeSerializationManager, typeof(SafeSerializationManager));
+ // _safeSerializationManager.CompleteSerialization(this, info, context);
+ // }
+ //
+ // 4. Add an OnDeserialized handler if one doesn't already exist, and call CompleteDeserialization in it:
+ //
+ // [OnDeserialized]
+ // private void OnDeserialized(StreamingContext context)
+ // {
+ // _safeSerializationManager.CompleteDeserialization(this);
+ // }
+ //
+ // On the client side, using safe serialization is also pretty easy. For example:
+ //
+ // [Serializable]
+ // public class TransparentException : Exception
+ // {
+ // [Serializable]
+ // private struct TransparentExceptionState : ISafeSerializationData
+ // {
+ // public string _extraData;
+ //
+ // void ISafeSerializationData.CompleteDeserialization(object obj)
+ // {
+ // TransparentException exception = obj as TransparentException;
+ // exception._state = this;
+ // }
+ // }
+ //
+ // [NonSerialized]
+ // private TransparentExceptionState _state = new TransparentExceptionState();
+ //
+ // public TransparentException()
+ // {
+ // SerializeObjectState += delegate(object exception, SafeSerializationEventArgs eventArgs)
+ // {
+ // eventArgs.AddSerializedState(_state);
+ // };
+ // }
+ //
+ // public string ExtraData
+ // {
+ // get { return _state._extraData; }
+ // set { _state._extraData = value; }
+ // }
+ // }
+ //
+
+ // Interface to be supported by objects which are stored in safe serialization stores
+ public interface ISafeSerializationData
+ {
+ // CompleteDeserialization is called when the object to which the extra serialized data was attached
+ // has completed its deserialization, and now needs to be populated with the extra data stored in
+ // this object.
+ void CompleteDeserialization(object deserialized);
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/ISerializable.cs b/src/mscorlib/shared/System/Runtime/Serialization/ISerializable.cs
new file mode 100644
index 0000000000..383b3f07af
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/ISerializable.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ public interface ISerializable
+ {
+ void GetObjectData(SerializationInfo info, StreamingContext context);
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializedAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializedAttribute.cs
new file mode 100644
index 0000000000..408a55ccf9
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializedAttribute.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+ public sealed class OnDeserializedAttribute : Attribute
+ {
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializingAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializingAttribute.cs
new file mode 100644
index 0000000000..162857e8d3
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/OnDeserializingAttribute.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+ public sealed class OnDeserializingAttribute : Attribute
+ {
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OnSerializedAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OnSerializedAttribute.cs
new file mode 100644
index 0000000000..020dd0257c
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/OnSerializedAttribute.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+ public sealed class OnSerializedAttribute : Attribute
+ {
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OnSerializingAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OnSerializingAttribute.cs
new file mode 100644
index 0000000000..8dc8af3f23
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/OnSerializingAttribute.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+ public sealed class OnSerializingAttribute : Attribute
+ {
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/OptionalFieldAttribute.cs b/src/mscorlib/shared/System/Runtime/Serialization/OptionalFieldAttribute.cs
new file mode 100644
index 0000000000..84daa539be
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/OptionalFieldAttribute.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ [AttributeUsage(AttributeTargets.Field, Inherited = false)]
+ public sealed class OptionalFieldAttribute : Attribute
+ {
+ private int _versionAdded = 1;
+
+ public int VersionAdded
+ {
+ get { return _versionAdded; }
+ set
+ {
+ if (value < 1)
+ {
+ throw new ArgumentException(SR.Serialization_OptionalFieldVersionValue);
+ }
+ _versionAdded = value;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/SafeSerializationEventArgs.cs b/src/mscorlib/shared/System/Runtime/Serialization/SafeSerializationEventArgs.cs
new file mode 100644
index 0000000000..896b91fca0
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/SafeSerializationEventArgs.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+
+namespace System.Runtime.Serialization
+{
+ // SafeSerializationEventArgs are provided to the delegates which do safe serialization. Each delegate
+ // serializes its own state into an IDeserializationCallback instance which must, itself, be serializable.
+ // These indivdiual states are then added to the SafeSerializationEventArgs in order to be saved away when
+ // the original ISerializable type is serialized.
+ public sealed class SafeSerializationEventArgs : EventArgs
+ {
+ private readonly List<object> _serializedStates = new List<object>();
+
+ internal SafeSerializationEventArgs() { }
+
+ public void AddSerializedState(ISafeSerializationData serializedState)
+ {
+ if (serializedState == null)
+ throw new ArgumentNullException(nameof(serializedState));
+ if (!serializedState.GetType().IsSerializable)
+ throw new ArgumentException(SR.Format(SR.Serialization_NonSerType, serializedState.GetType(), serializedState.GetType().Assembly.FullName));
+
+ _serializedStates.Add(serializedState);
+ }
+
+ public StreamingContext StreamingContext { get; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs b/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs
new file mode 100644
index 0000000000..a359daf4f9
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/SerializationException.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Runtime.Serialization
+{
+ [Serializable]
+ public class SerializationException : SystemException
+ {
+ private static String s_nullMessage = SR.SerializationException;
+
+ // Creates a new SerializationException with its message
+ // string set to a default message.
+ public SerializationException()
+ : base(s_nullMessage)
+ {
+ HResult = __HResults.COR_E_SERIALIZATION;
+ }
+
+ public SerializationException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_SERIALIZATION;
+ }
+
+ public SerializationException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_SERIALIZATION;
+ }
+
+ protected SerializationException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/SerializationInfoEnumerator.cs b/src/mscorlib/shared/System/Runtime/Serialization/SerializationInfoEnumerator.cs
new file mode 100644
index 0000000000..6399510736
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/SerializationInfoEnumerator.cs
@@ -0,0 +1,127 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections;
+using System.Diagnostics;
+
+namespace System.Runtime.Serialization
+{
+ public struct SerializationEntry
+ {
+ private string _name;
+ private object _value;
+ private Type _type;
+
+ internal SerializationEntry(string entryName, object entryValue, Type entryType)
+ {
+ _name = entryName;
+ _value = entryValue;
+ _type = entryType;
+ }
+
+ public object Value => _value;
+ public string Name => _name;
+ public Type ObjectType => _type;
+ }
+
+ public sealed class SerializationInfoEnumerator : IEnumerator
+ {
+ private readonly string[] _members;
+ private readonly object[] _data;
+ private readonly Type[] _types;
+ private readonly int _numItems;
+ private int _currItem;
+ private bool _current;
+
+ internal SerializationInfoEnumerator(string[] members, object[] info, Type[] types, int numItems)
+ {
+ Debug.Assert(members != null, "[SerializationInfoEnumerator.ctor]members!=null");
+ Debug.Assert(info != null, "[SerializationInfoEnumerator.ctor]info!=null");
+ Debug.Assert(types != null, "[SerializationInfoEnumerator.ctor]types!=null");
+ Debug.Assert(numItems >= 0, "[SerializationInfoEnumerator.ctor]numItems>=0");
+ Debug.Assert(members.Length >= numItems, "[SerializationInfoEnumerator.ctor]members.Length>=numItems");
+ Debug.Assert(info.Length >= numItems, "[SerializationInfoEnumerator.ctor]info.Length>=numItems");
+ Debug.Assert(types.Length >= numItems, "[SerializationInfoEnumerator.ctor]types.Length>=numItems");
+
+ _members = members;
+ _data = info;
+ _types = types;
+
+ //The MoveNext semantic is much easier if we enforce that [0..m_numItems] are valid entries
+ //in the enumerator, hence we subtract 1.
+ _numItems = numItems - 1;
+ _currItem = -1;
+ _current = false;
+ }
+
+ public bool MoveNext()
+ {
+ if (_currItem < _numItems)
+ {
+ _currItem++;
+ _current = true;
+ }
+ else
+ {
+ _current = false;
+ }
+
+ return _current;
+ }
+
+ object IEnumerator.Current => Current;
+
+ public SerializationEntry Current
+ {
+ get
+ {
+ if (_current == false)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen);
+ }
+ return new SerializationEntry(_members[_currItem], _data[_currItem], _types[_currItem]);
+ }
+ }
+
+ public void Reset()
+ {
+ _currItem = -1;
+ _current = false;
+ }
+
+ public string Name
+ {
+ get
+ {
+ if (_current == false)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen);
+ }
+ return _members[_currItem];
+ }
+ }
+ public object Value
+ {
+ get
+ {
+ if (_current == false)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen);
+ }
+ return _data[_currItem];
+ }
+ }
+ public Type ObjectType
+ {
+ get
+ {
+ if (_current == false)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_EnumOpCantHappen);
+ }
+ return _types[_currItem];
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Serialization/StreamingContext.cs b/src/mscorlib/shared/System/Runtime/Serialization/StreamingContext.cs
new file mode 100644
index 0000000000..1026a87d1e
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Serialization/StreamingContext.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Runtime.Serialization
+{
+ [Serializable]
+ public struct StreamingContext
+ {
+ private readonly object _additionalContext;
+ private readonly StreamingContextStates _state;
+
+ public StreamingContext(StreamingContextStates state) : this(state, null)
+ {
+ }
+
+ public StreamingContext(StreamingContextStates state, object additional)
+ {
+ _state = state;
+ _additionalContext = additional;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (!(obj is StreamingContext))
+ {
+ return false;
+ }
+ StreamingContext ctx = (StreamingContext)obj;
+ return ctx._additionalContext == _additionalContext && ctx._state == _state;
+ }
+
+ public override int GetHashCode() => (int)_state;
+
+ public StreamingContextStates State => _state;
+
+ public object Context => _additionalContext;
+ }
+
+ [Flags]
+ public enum StreamingContextStates
+ {
+ CrossProcess = 0x01,
+ CrossMachine = 0x02,
+ File = 0x04,
+ Persistence = 0x08,
+ Remoting = 0x10,
+ Other = 0x20,
+ Clone = 0x40,
+ CrossAppDomain = 0x80,
+ All = 0xFF,
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Versioning/NonVersionableAttribute.cs b/src/mscorlib/shared/System/Runtime/Versioning/NonVersionableAttribute.cs
new file mode 100644
index 0000000000..e4809953bc
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Versioning/NonVersionableAttribute.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** The [NonVersionable] attribute is applied to indicate that the implementation
+** of a particular member or layout of a struct cannot be changed for given platform in incompatible way.
+** This allows cross-module inlining of methods and data structures whose implementation
+** is never changed in ReadyToRun native images. Any changes to such members or types would be
+** breaking changes for ReadyToRun.
+**
+** Applying this type also has the side effect that the inlining tables in R2R images will not
+** report that inlining of NonVersionable attributed methods occured. These inlining tables are used
+** by profilers to figure out the set of methods that need to be rejited when one method is instrumented,
+** so in effect NonVersionable methods are also non-instrumentable. Generally this is OK for
+** extremely trivial low level methods where NonVersionable gets used, but if there is any plan to
+** significantly extend its usage or allow 3rd parties to use it please discuss with the diagnostics team.
+===========================================================*/
+
+using System;
+using System.Diagnostics;
+
+namespace System.Runtime.Versioning
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor,
+ AllowMultiple = false, Inherited = false)]
+ internal sealed class NonVersionableAttribute : Attribute
+ {
+ public NonVersionableAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Runtime/Versioning/TargetFrameworkAttribute.cs b/src/mscorlib/shared/System/Runtime/Versioning/TargetFrameworkAttribute.cs
new file mode 100644
index 0000000000..54ccdf2c81
--- /dev/null
+++ b/src/mscorlib/shared/System/Runtime/Versioning/TargetFrameworkAttribute.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Identifies which SKU and version of the .NET
+** Framework that a particular library was compiled against.
+** Emitted by VS, and can help catch deployment problems.
+**
+===========================================================*/
+
+using System;
+using System.Diagnostics.Contracts;
+
+namespace System.Runtime.Versioning
+{
+ [AttributeUsageAttribute(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
+ public sealed class TargetFrameworkAttribute : Attribute
+ {
+ private String _frameworkName; // A target framework moniker
+ private String _frameworkDisplayName;
+
+ // The frameworkName parameter is intended to be the string form of a FrameworkName instance.
+ public TargetFrameworkAttribute(String frameworkName)
+ {
+ if (frameworkName == null)
+ throw new ArgumentNullException(nameof(frameworkName));
+ Contract.EndContractBlock();
+ _frameworkName = frameworkName;
+ }
+
+ // The target framework moniker that this assembly was compiled against.
+ // Use the FrameworkName class to interpret target framework monikers.
+ public String FrameworkName
+ {
+ get { return _frameworkName; }
+ }
+
+ public String FrameworkDisplayName
+ {
+ get { return _frameworkDisplayName; }
+ set { _frameworkDisplayName = value; }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Security/AllowPartiallyTrustedCallersAttribute.cs b/src/mscorlib/shared/System/Security/AllowPartiallyTrustedCallersAttribute.cs
new file mode 100644
index 0000000000..84ad65c4c0
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/AllowPartiallyTrustedCallersAttribute.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ // AllowPartiallyTrustedCallersAttribute:
+ // Indicates that the Assembly is secure and can be used by untrusted
+ // and semitrusted clients
+ // For v.1, this is valid only on Assemblies, but could be expanded to
+ // include Module, Method, class
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
+ public sealed class AllowPartiallyTrustedCallersAttribute : Attribute
+ {
+ public AllowPartiallyTrustedCallersAttribute() { }
+ public PartialTrustVisibilityLevel PartialTrustVisibilityLevel { get; set; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/CryptographicException.cs b/src/mscorlib/shared/System/Security/CryptographicException.cs
new file mode 100644
index 0000000000..89cb658aa9
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/CryptographicException.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Globalization;
+using System.Runtime.Serialization;
+
+namespace System.Security.Cryptography
+{
+ [Serializable]
+ public class CryptographicException : SystemException
+ {
+ public CryptographicException()
+ : base(SR.Arg_CryptographyException)
+ {
+ }
+
+ public CryptographicException(int hr)
+ : base(SR.Arg_CryptographyException)
+ {
+ HResult = hr;
+ }
+
+ public CryptographicException(string message)
+ : base(message)
+ {
+ }
+
+ public CryptographicException(string message, Exception inner)
+ : base(message, inner)
+ {
+ }
+
+ public CryptographicException(string format, string insert)
+ : base(string.Format(CultureInfo.CurrentCulture, format, insert))
+ {
+ }
+
+ protected CryptographicException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Security/PartialTrustVisibilityLevel.cs b/src/mscorlib/shared/System/Security/PartialTrustVisibilityLevel.cs
new file mode 100644
index 0000000000..a0cb5789ac
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/PartialTrustVisibilityLevel.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ public enum PartialTrustVisibilityLevel
+ {
+ VisibleToAllHosts = 0,
+ NotVisibleByDefault = 1
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/SafeBSTRHandle.cs b/src/mscorlib/shared/System/Security/SafeBSTRHandle.cs
new file mode 100644
index 0000000000..a1164dce91
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SafeBSTRHandle.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace System.Security
+{
+ internal sealed class SafeBSTRHandle : SafeBuffer
+ {
+ internal SafeBSTRHandle() : base(true) { }
+
+ internal static SafeBSTRHandle Allocate(uint lenInChars)
+ {
+ ulong lenInBytes = (ulong)lenInChars * sizeof(char);
+ SafeBSTRHandle bstr = Interop.OleAut32.SysAllocStringLen(IntPtr.Zero, lenInChars);
+ if (bstr.IsInvalid) // SysAllocStringLen returns a NULL ptr when there's insufficient memory
+ {
+ throw new OutOfMemoryException();
+ }
+ bstr.Initialize(lenInBytes);
+ return bstr;
+ }
+
+ override protected bool ReleaseHandle()
+ {
+ Interop.NtDll.ZeroMemory(handle, (UIntPtr)(Interop.OleAut32.SysStringLen(handle) * sizeof(char)));
+ Interop.OleAut32.SysFreeString(handle);
+ return true;
+ }
+
+ internal unsafe void ClearBuffer()
+ {
+ byte* bufferPtr = null;
+ try
+ {
+ AcquirePointer(ref bufferPtr);
+ Interop.NtDll.ZeroMemory((IntPtr)bufferPtr, (UIntPtr)(Interop.OleAut32.SysStringLen((IntPtr)bufferPtr) * sizeof(char)));
+ }
+ finally
+ {
+ if (bufferPtr != null)
+ {
+ ReleasePointer();
+ }
+ }
+ }
+
+ internal unsafe uint Length => Interop.OleAut32.SysStringLen(this);
+
+ internal unsafe static void Copy(SafeBSTRHandle source, SafeBSTRHandle target, uint bytesToCopy)
+ {
+ if (bytesToCopy == 0)
+ {
+ return;
+ }
+
+ byte* sourcePtr = null, targetPtr = null;
+ try
+ {
+ source.AcquirePointer(ref sourcePtr);
+ target.AcquirePointer(ref targetPtr);
+
+ Debug.Assert(source.ByteLength >= bytesToCopy, "Source buffer is too small.");
+ Buffer.MemoryCopy(sourcePtr, targetPtr, target.ByteLength, bytesToCopy);
+ }
+ finally
+ {
+ if (targetPtr != null)
+ {
+ target.ReleasePointer();
+ }
+ if (sourcePtr != null)
+ {
+ source.ReleasePointer();
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Security/SecureString.Unix.cs b/src/mscorlib/shared/System/Security/SecureString.Unix.cs
new file mode 100644
index 0000000000..0ef38e40ee
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecureString.Unix.cs
@@ -0,0 +1,295 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace System.Security
+{
+ // SecureString attempts to provide a defense-in-depth solution.
+ //
+ // On Windows, this is done with several mechanisms:
+ // 1. keeping the data in unmanaged memory so that copies of it aren't implicitly made by the GC moving it around
+ // 2. zero'ing out that unmanaged memory so that the string is reliably removed from memory when done with it
+ // 3. encrypting the data while it's not being used (it's unencrypted to manipulate and use it)
+ //
+ // On Unix, we do 1 and 2, but we don't do 3 as there's no CryptProtectData equivalent.
+
+ public sealed partial class SecureString
+ {
+ private UnmanagedBuffer _buffer;
+
+ internal SecureString(SecureString str)
+ {
+ // Allocate enough space to store the provided string
+ EnsureCapacity(str._decryptedLength);
+ _decryptedLength = str._decryptedLength;
+
+ // Copy the string into the newly allocated space
+ if (_decryptedLength > 0)
+ {
+ UnmanagedBuffer.Copy(str._buffer, _buffer, (ulong)(str._decryptedLength * sizeof(char)));
+ }
+ }
+
+ private unsafe void InitializeSecureString(char* value, int length)
+ {
+ // Allocate enough space to store the provided string
+ EnsureCapacity(length);
+ _decryptedLength = length;
+ if (length == 0)
+ {
+ return;
+ }
+
+ // Copy the string into the newly allocated space
+ byte* ptr = null;
+ try
+ {
+ _buffer.AcquirePointer(ref ptr);
+ Buffer.MemoryCopy(value, ptr, _buffer.ByteLength, (ulong)(length * sizeof(char)));
+ }
+ finally
+ {
+ if (ptr != null)
+ {
+ _buffer.ReleasePointer();
+ }
+ }
+ }
+
+ private void DisposeCore()
+ {
+ if (_buffer != null && !_buffer.IsInvalid)
+ {
+ _buffer.Dispose();
+ _buffer = null;
+ }
+ }
+
+ private void EnsureNotDisposed()
+ {
+ if (_buffer == null)
+ {
+ throw new ObjectDisposedException(GetType().Name);
+ }
+ }
+
+ private void ClearCore()
+ {
+ _decryptedLength = 0;
+ _buffer.Clear();
+ }
+
+ private unsafe void AppendCharCore(char c)
+ {
+ // Make sure we have enough space for the new character, then write it at the end.
+ EnsureCapacity(_decryptedLength + 1);
+ _buffer.Write((ulong)(_decryptedLength * sizeof(char)), c);
+ _decryptedLength++;
+ }
+
+ private unsafe void InsertAtCore(int index, char c)
+ {
+ // Make sure we have enough space for the new character, then shift all of the characters above it and insert it.
+ EnsureCapacity(_decryptedLength + 1);
+ byte* ptr = null;
+ try
+ {
+ _buffer.AcquirePointer(ref ptr);
+ ptr += index * sizeof(char);
+ long bytesToShift = (_decryptedLength - index) * sizeof(char);
+ Buffer.MemoryCopy(ptr, ptr + sizeof(char), bytesToShift, bytesToShift);
+ *((char*)ptr) = c;
+ ++_decryptedLength;
+ }
+ finally
+ {
+ if (ptr != null)
+ {
+ _buffer.ReleasePointer();
+ }
+ }
+ }
+
+ private unsafe void RemoveAtCore(int index)
+ {
+ // Shift down all values above the specified index, then null out the empty space at the end.
+ byte* ptr = null;
+ try
+ {
+ _buffer.AcquirePointer(ref ptr);
+ ptr += index * sizeof(char);
+ long bytesToShift = (_decryptedLength - index - 1) * sizeof(char);
+ Buffer.MemoryCopy(ptr + sizeof(char), ptr, bytesToShift, bytesToShift);
+ *((char*)(ptr + bytesToShift)) = (char)0;
+ --_decryptedLength;
+ }
+ finally
+ {
+ if (ptr != null)
+ {
+ _buffer.ReleasePointer();
+ }
+ }
+ }
+
+ private void SetAtCore(int index, char c)
+ {
+ // Overwrite the character at the specified index
+ _buffer.Write((ulong)(index * sizeof(char)), c);
+ }
+
+ internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode)
+ {
+ int length = _decryptedLength;
+
+ byte* bufferPtr = null;
+ IntPtr stringPtr = IntPtr.Zero, result = IntPtr.Zero;
+ try
+ {
+ _buffer.AcquirePointer(ref bufferPtr);
+ if (unicode)
+ {
+ int resultLength = (length + 1) * sizeof(char);
+ stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength);
+ Buffer.MemoryCopy(
+ source: bufferPtr,
+ destination: (byte*)stringPtr.ToPointer(),
+ destinationSizeInBytes: resultLength,
+ sourceBytesToCopy: length * sizeof(char));
+ *(length + (char*)stringPtr) = '\0';
+ }
+ else
+ {
+ int resultLength = Encoding.UTF8.GetByteCount((char*)bufferPtr, length) + 1;
+ stringPtr = globalAlloc ? Marshal.AllocHGlobal(resultLength) : Marshal.AllocCoTaskMem(resultLength);
+ int encodedLength = Encoding.UTF8.GetBytes((char*)bufferPtr, length, (byte*)stringPtr, resultLength);
+ Debug.Assert(encodedLength + 1 == resultLength, $"Expected encoded length to match result, got {encodedLength} != {resultLength}");
+ *(resultLength - 1 + (byte*)stringPtr) = 0;
+ }
+
+ result = stringPtr;
+ }
+ finally
+ {
+ // If there was a failure, such that result isn't initialized,
+ // release the string if we had one.
+ if (stringPtr != IntPtr.Zero && result == IntPtr.Zero)
+ {
+ UnmanagedBuffer.ZeroMemory((byte*)stringPtr, (ulong)(length * sizeof(char)));
+ MarshalFree(stringPtr, globalAlloc);
+ }
+
+ if (bufferPtr != null)
+ {
+ _buffer.ReleasePointer();
+ }
+ }
+
+ return result;
+ }
+
+ // -----------------------------
+ // ---- PAL layer ends here ----
+ // -----------------------------
+
+ private void EnsureCapacity(int capacity)
+ {
+ // Make sure the requested capacity doesn't exceed SecureString's defined limit
+ if (capacity > MaxLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity);
+ }
+
+ // If we already have enough space allocated, we're done
+ if (_buffer != null && (capacity * sizeof(char)) <= (int)_buffer.ByteLength)
+ {
+ return;
+ }
+
+ // We need more space, so allocate a new buffer, copy all our data into it,
+ // and then swap the new for the old.
+ UnmanagedBuffer newBuffer = UnmanagedBuffer.Allocate(capacity * sizeof(char));
+ if (_buffer != null)
+ {
+ UnmanagedBuffer.Copy(_buffer, newBuffer, _buffer.ByteLength);
+ _buffer.Dispose();
+ }
+ _buffer = newBuffer;
+ }
+
+ /// <summary>SafeBuffer for managing memory meant to be kept confidential.</summary>
+ private sealed class UnmanagedBuffer : SafeBuffer
+ {
+ internal UnmanagedBuffer() : base(true) { }
+
+ internal static UnmanagedBuffer Allocate(int bytes)
+ {
+ Debug.Assert(bytes >= 0);
+ UnmanagedBuffer buffer = new UnmanagedBuffer();
+ buffer.SetHandle(Marshal.AllocHGlobal(bytes));
+ buffer.Initialize((ulong)bytes);
+ return buffer;
+ }
+
+ internal unsafe void Clear()
+ {
+ byte* ptr = null;
+ try
+ {
+ AcquirePointer(ref ptr);
+ ZeroMemory(ptr, ByteLength);
+ }
+ finally
+ {
+ if (ptr != null)
+ {
+ ReleasePointer();
+ }
+ }
+ }
+
+ internal static unsafe void Copy(UnmanagedBuffer source, UnmanagedBuffer destination, ulong bytesLength)
+ {
+ if (bytesLength == 0)
+ {
+ return;
+ }
+
+ byte* srcPtr = null, dstPtr = null;
+ try
+ {
+ source.AcquirePointer(ref srcPtr);
+ destination.AcquirePointer(ref dstPtr);
+ Buffer.MemoryCopy(srcPtr, dstPtr, destination.ByteLength, bytesLength);
+ }
+ finally
+ {
+ if (dstPtr != null)
+ {
+ destination.ReleasePointer();
+ }
+ if (srcPtr != null)
+ {
+ source.ReleasePointer();
+ }
+ }
+ }
+
+ protected override unsafe bool ReleaseHandle()
+ {
+ Marshal.FreeHGlobal(handle);
+ return true;
+ }
+
+ internal static unsafe void ZeroMemory(byte* ptr, ulong len)
+ {
+ for (ulong i = 0; i < len; i++) *ptr++ = 0;
+ }
+ }
+
+ }
+}
diff --git a/src/mscorlib/shared/System/Security/SecureString.Windows.cs b/src/mscorlib/shared/System/Security/SecureString.Windows.cs
new file mode 100644
index 0000000000..13f75a37e9
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecureString.Windows.cs
@@ -0,0 +1,311 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using Microsoft.Win32;
+
+namespace System.Security
+{
+ public sealed partial class SecureString
+ {
+ internal SecureString(SecureString str)
+ {
+ Debug.Assert(str != null, "Expected non-null SecureString");
+ Debug.Assert(str._buffer != null, "Expected other SecureString's buffer to be non-null");
+ Debug.Assert(str._encrypted, "Expected to be used only on encrypted SecureStrings");
+
+ AllocateBuffer(str._buffer.Length);
+ SafeBSTRHandle.Copy(str._buffer, _buffer, str._buffer.Length * sizeof(char));
+
+ _decryptedLength = str._decryptedLength;
+ _encrypted = str._encrypted;
+ }
+
+ private unsafe void InitializeSecureString(char* value, int length)
+ {
+ Debug.Assert(length >= 0, $"Expected non-negative length, got {length}");
+
+ AllocateBuffer((uint)length);
+ _decryptedLength = length;
+
+ byte* bufferPtr = null;
+ try
+ {
+ _buffer.AcquirePointer(ref bufferPtr);
+ Buffer.MemoryCopy((byte*)value, bufferPtr, (long)_buffer.ByteLength, length * sizeof(char));
+ }
+ finally
+ {
+ if (bufferPtr != null)
+ {
+ _buffer.ReleasePointer();
+ }
+ }
+
+ ProtectMemory();
+ }
+
+ private void AppendCharCore(char c)
+ {
+ UnprotectMemory();
+ try
+ {
+ EnsureCapacity(_decryptedLength + 1);
+ _buffer.Write<char>((uint)_decryptedLength * sizeof(char), c);
+ _decryptedLength++;
+ }
+ finally
+ {
+ ProtectMemory();
+ }
+ }
+
+ private void ClearCore()
+ {
+ _decryptedLength = 0;
+ _buffer.ClearBuffer();
+ }
+
+ private void DisposeCore()
+ {
+ if (_buffer != null)
+ {
+ _buffer.Dispose();
+ _buffer = null;
+ }
+ }
+
+ private unsafe void InsertAtCore(int index, char c)
+ {
+ byte* bufferPtr = null;
+ UnprotectMemory();
+ try
+ {
+ EnsureCapacity(_decryptedLength + 1);
+
+ _buffer.AcquirePointer(ref bufferPtr);
+ char* pBuffer = (char*)bufferPtr;
+
+ for (int i = _decryptedLength; i > index; i--)
+ {
+ pBuffer[i] = pBuffer[i - 1];
+ }
+ pBuffer[index] = c;
+ ++_decryptedLength;
+ }
+ finally
+ {
+ ProtectMemory();
+ if (bufferPtr != null)
+ {
+ _buffer.ReleasePointer();
+ }
+ }
+ }
+
+ private unsafe void RemoveAtCore(int index)
+ {
+ byte* bufferPtr = null;
+ UnprotectMemory();
+ try
+ {
+ _buffer.AcquirePointer(ref bufferPtr);
+ char* pBuffer = (char*)bufferPtr;
+
+ for (int i = index; i < _decryptedLength - 1; i++)
+ {
+ pBuffer[i] = pBuffer[i + 1];
+ }
+ pBuffer[--_decryptedLength] = (char)0;
+ }
+ finally
+ {
+ ProtectMemory();
+ if (bufferPtr != null)
+ {
+ _buffer.ReleasePointer();
+ }
+ }
+ }
+
+ private void SetAtCore(int index, char c)
+ {
+ UnprotectMemory();
+ try
+ {
+ _buffer.Write<char>((uint)index * sizeof(char), c);
+ }
+ finally
+ {
+ ProtectMemory();
+ }
+ }
+
+ internal unsafe IntPtr MarshalToBSTR()
+ {
+ int length = _decryptedLength;
+ IntPtr ptr = IntPtr.Zero;
+ IntPtr result = IntPtr.Zero;
+ byte* bufferPtr = null;
+
+ UnprotectMemory();
+ try
+ {
+ _buffer.AcquirePointer(ref bufferPtr);
+ int resultByteLength = (length + 1) * sizeof(char);
+
+ ptr = Interop.OleAut32.SysAllocStringLen(null, length);
+ if (ptr == IntPtr.Zero)
+ {
+ throw new OutOfMemoryException();
+ }
+
+ Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char));
+
+ result = ptr;
+ }
+ finally
+ {
+ ProtectMemory();
+
+ // If we failed for any reason, free the new buffer
+ if (result == IntPtr.Zero && ptr != IntPtr.Zero)
+ {
+ Interop.NtDll.ZeroMemory(ptr, (UIntPtr)(length * sizeof(char)));
+ Interop.OleAut32.SysFreeString(ptr);
+ }
+
+ if (bufferPtr != null)
+ {
+ _buffer.ReleasePointer();
+ }
+ }
+ return result;
+ }
+
+ internal unsafe IntPtr MarshalToStringCore(bool globalAlloc, bool unicode)
+ {
+ int length = _decryptedLength;
+ IntPtr ptr = IntPtr.Zero;
+ IntPtr result = IntPtr.Zero;
+ byte* bufferPtr = null;
+
+ UnprotectMemory();
+ try
+ {
+ _buffer.AcquirePointer(ref bufferPtr);
+ if (unicode)
+ {
+ int resultByteLength = (length + 1) * sizeof(char);
+ ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength);
+ Buffer.MemoryCopy(bufferPtr, (byte*)ptr, resultByteLength, length * sizeof(char));
+ *(length + (char*)ptr) = '\0';
+ }
+ else
+ {
+ uint defaultChar = '?';
+ int resultByteLength = 1 + Interop.Kernel32.WideCharToMultiByte(
+ Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, null, 0, (IntPtr)(&defaultChar), IntPtr.Zero);
+ ptr = globalAlloc ? Marshal.AllocHGlobal(resultByteLength) : Marshal.AllocCoTaskMem(resultByteLength);
+ Interop.Kernel32.WideCharToMultiByte(
+ Interop.Kernel32.CP_ACP, Interop.Kernel32.WC_NO_BEST_FIT_CHARS, (char*)bufferPtr, length, (byte*)ptr, resultByteLength - 1, (IntPtr)(&defaultChar), IntPtr.Zero);
+ *(resultByteLength - 1 + (byte*)ptr) = 0;
+ }
+ result = ptr;
+ }
+ finally
+ {
+ ProtectMemory();
+
+ // If we failed for any reason, free the new buffer
+ if (result == IntPtr.Zero && ptr != IntPtr.Zero)
+ {
+ Interop.NtDll.ZeroMemory(ptr, (UIntPtr)(length * sizeof(char)));
+ MarshalFree(ptr, globalAlloc);
+ }
+
+ if (bufferPtr != null)
+ {
+ _buffer.ReleasePointer();
+ }
+ }
+ return result;
+ }
+
+ private void EnsureNotDisposed()
+ {
+ if (_buffer == null)
+ {
+ throw new ObjectDisposedException(GetType().Name);
+ }
+ }
+
+ // -----------------------------
+ // ---- PAL layer ends here ----
+ // -----------------------------
+
+ private const int BlockSize = (int)Interop.Crypt32.CRYPTPROTECTMEMORY_BLOCK_SIZE / sizeof(char);
+ private SafeBSTRHandle _buffer;
+ private bool _encrypted;
+
+ private void AllocateBuffer(uint size)
+ {
+ _buffer = SafeBSTRHandle.Allocate(GetAlignedSize(size));
+ }
+
+ private static uint GetAlignedSize(uint size) =>
+ size == 0 || size % BlockSize != 0 ?
+ BlockSize + ((size / BlockSize) * BlockSize) :
+ size;
+
+ private void EnsureCapacity(int capacity)
+ {
+ if (capacity > MaxLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity);
+ }
+
+ if (((uint)capacity * sizeof(char)) <= _buffer.ByteLength)
+ {
+ return;
+ }
+
+ var oldBuffer = _buffer;
+ SafeBSTRHandle newBuffer = SafeBSTRHandle.Allocate(GetAlignedSize((uint)capacity));
+ SafeBSTRHandle.Copy(oldBuffer, newBuffer, (uint)_decryptedLength * sizeof(char));
+ _buffer = newBuffer;
+ oldBuffer.Dispose();
+ }
+
+ private void ProtectMemory()
+ {
+ Debug.Assert(!_buffer.IsInvalid, "Invalid buffer!");
+
+ if (_decryptedLength != 0 &&
+ !_encrypted &&
+ !Interop.Crypt32.CryptProtectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS))
+ {
+ throw new CryptographicException(Marshal.GetLastWin32Error());
+ }
+
+ _encrypted = true;
+ }
+
+ private void UnprotectMemory()
+ {
+ Debug.Assert(!_buffer.IsInvalid, "Invalid buffer!");
+
+ if (_decryptedLength != 0 &&
+ _encrypted &&
+ !Interop.Crypt32.CryptUnprotectMemory(_buffer, _buffer.Length * sizeof(char), Interop.Crypt32.CRYPTPROTECTMEMORY_SAME_PROCESS))
+ {
+ throw new CryptographicException(Marshal.GetLastWin32Error());
+ }
+
+ _encrypted = false;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Security/SecureString.cs b/src/mscorlib/shared/System/Security/SecureString.cs
new file mode 100644
index 0000000000..9059f90e60
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecureString.cs
@@ -0,0 +1,189 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace System.Security
+{
+ public sealed partial class SecureString : IDisposable
+ {
+ private const int MaxLength = 65536;
+ private readonly object _methodLock = new object();
+ private bool _readOnly;
+ private int _decryptedLength;
+
+ public unsafe SecureString()
+ {
+ InitializeSecureString(null, 0);
+ }
+
+ [CLSCompliant(false)]
+ public unsafe SecureString(char* value, int length)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ if (length > MaxLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Length);
+ }
+
+ InitializeSecureString(value, length);
+ }
+
+ public int Length
+ {
+ get
+ {
+ lock (_methodLock)
+ {
+ EnsureNotDisposed();
+ return _decryptedLength;
+ }
+ }
+ }
+
+ public void AppendChar(char c)
+ {
+ lock (_methodLock)
+ {
+ EnsureNotDisposed();
+ EnsureNotReadOnly();
+ AppendCharCore(c);
+ }
+ }
+
+ // clears the current contents. Only available if writable
+ public void Clear()
+ {
+ lock (_methodLock)
+ {
+ EnsureNotDisposed();
+ EnsureNotReadOnly();
+ ClearCore();
+ }
+ }
+
+ // Do a deep-copy of the SecureString
+ public SecureString Copy()
+ {
+ lock (_methodLock)
+ {
+ EnsureNotDisposed();
+ return new SecureString(this);
+ }
+ }
+
+ public void Dispose()
+ {
+ lock (_methodLock)
+ {
+ DisposeCore();
+ }
+ }
+
+ public void InsertAt(int index, char c)
+ {
+ lock (_methodLock)
+ {
+ if (index < 0 || index > _decryptedLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString);
+ }
+
+ EnsureNotDisposed();
+ EnsureNotReadOnly();
+
+ InsertAtCore(index, c);
+ }
+ }
+
+ public bool IsReadOnly()
+ {
+ lock (_methodLock)
+ {
+ EnsureNotDisposed();
+ return _readOnly;
+ }
+ }
+
+ public void MakeReadOnly()
+ {
+ lock (_methodLock)
+ {
+ EnsureNotDisposed();
+ _readOnly = true;
+ }
+ }
+
+ public void RemoveAt(int index)
+ {
+ lock (_methodLock)
+ {
+ EnsureNotDisposed();
+ EnsureNotReadOnly();
+
+ if (index < 0 || index >= _decryptedLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString);
+ }
+
+ RemoveAtCore(index);
+ }
+ }
+
+ public void SetAt(int index, char c)
+ {
+ lock (_methodLock)
+ {
+ if (index < 0 || index >= _decryptedLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_IndexString);
+ }
+ Debug.Assert(index <= Int32.MaxValue / sizeof(char));
+
+ EnsureNotDisposed();
+ EnsureNotReadOnly();
+
+ SetAtCore(index, c);
+ }
+ }
+
+ private void EnsureNotReadOnly()
+ {
+ if (_readOnly)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
+ }
+ }
+
+ internal unsafe IntPtr MarshalToString(bool globalAlloc, bool unicode)
+ {
+ lock (_methodLock)
+ {
+ EnsureNotDisposed();
+ return MarshalToStringCore(globalAlloc, unicode);
+ }
+ }
+
+ private static void MarshalFree(IntPtr ptr, bool globalAlloc)
+ {
+ if (globalAlloc)
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+ else
+ {
+ Marshal.FreeCoTaskMem(ptr);
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Security/SecurityCriticalAttribute.cs b/src/mscorlib/shared/System/Security/SecurityCriticalAttribute.cs
new file mode 100644
index 0000000000..2bf1700afb
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecurityCriticalAttribute.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ // SecurityCriticalAttribute
+ // Indicates that the decorated code or assembly performs security critical operations (e.g. Assert, "unsafe", LinkDemand, etc.)
+ // The attribute can be placed on most targets, except on arguments/return values.
+ [AttributeUsage(AttributeTargets.Assembly |
+ AttributeTargets.Class |
+ AttributeTargets.Struct |
+ AttributeTargets.Enum |
+ AttributeTargets.Constructor |
+ AttributeTargets.Method |
+ AttributeTargets.Field |
+ AttributeTargets.Interface |
+ AttributeTargets.Delegate,
+ AllowMultiple = false,
+ Inherited = false)]
+ public sealed class SecurityCriticalAttribute : Attribute
+ {
+#pragma warning disable 618 // We still use SecurityCriticalScope for v2 compat
+ public SecurityCriticalAttribute() { }
+
+ public SecurityCriticalAttribute(SecurityCriticalScope scope)
+ {
+ Scope = scope;
+ }
+
+ [Obsolete("SecurityCriticalScope is only used for .NET 2.0 transparency compatibility.")]
+ public SecurityCriticalScope Scope { get; }
+#pragma warning restore 618
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/SecurityCriticalScope.cs b/src/mscorlib/shared/System/Security/SecurityCriticalScope.cs
new file mode 100644
index 0000000000..e0f5a8e2cd
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecurityCriticalScope.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ [Obsolete("SecurityCriticalScope is only used for .NET 2.0 transparency compatibility.")]
+ public enum SecurityCriticalScope
+ {
+ Explicit = 0,
+ Everything = 0x1
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/SecurityException.cs b/src/mscorlib/shared/System/Security/SecurityException.cs
new file mode 100644
index 0000000000..86e3cd4631
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecurityException.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Reflection;
+using System.Runtime.Serialization;
+
+namespace System.Security
+{
+ [Serializable]
+ public class SecurityException : SystemException
+ {
+ public SecurityException()
+ : base(SR.Arg_SecurityException)
+ {
+ HResult = __HResults.COR_E_SECURITY;
+ }
+
+ public SecurityException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_SECURITY;
+ }
+
+ public SecurityException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_SECURITY;
+ }
+
+ public SecurityException(string message, Type type)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_SECURITY;
+ PermissionType = type;
+ }
+
+ public SecurityException(string message, Type type, string state)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_SECURITY;
+ PermissionType = type;
+ PermissionState = state;
+ }
+
+ protected SecurityException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ public override string ToString() => base.ToString();
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context) => base.GetObjectData(info, context);
+
+ public object Demanded { get; set; }
+ public object DenySetInstance { get; set; }
+ public AssemblyName FailedAssemblyInfo { get; set; }
+ public string GrantedSet { get; set; }
+ public MethodInfo Method { get; set; }
+ public string PermissionState { get; set; }
+ public Type PermissionType { get; set; }
+ public object PermitOnlySetInstance { get; set; }
+ public string RefusedSet { get; set; }
+ public string Url { get; set; }
+ }
+}
diff --git a/src/mscorlib/shared/System/Security/SecurityRuleSet.cs b/src/mscorlib/shared/System/Security/SecurityRuleSet.cs
new file mode 100644
index 0000000000..1b62fd4e7d
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecurityRuleSet.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ public enum SecurityRuleSet : byte
+ {
+ None = 0,
+ Level1 = 1, // v2.0 transparency model
+ Level2 = 2, // v4.0 transparency model
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/SecurityRulesAttribute.cs b/src/mscorlib/shared/System/Security/SecurityRulesAttribute.cs
new file mode 100644
index 0000000000..ad17087f8b
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecurityRulesAttribute.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ // SecurityRulesAttribute
+ //
+ // Indicates which set of security rules an assembly was authored against, and therefore which set of
+ // rules the runtime should enforce on the assembly. For instance, an assembly marked with
+ // [SecurityRules(SecurityRuleSet.Level1)] will follow the v2.0 transparency rules, where transparent code
+ // can call a LinkDemand by converting it to a full demand, public critical methods are implicitly
+ // treat as safe, and the remainder of the v2.0 rules apply.
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
+ public sealed class SecurityRulesAttribute : Attribute
+ {
+ public SecurityRulesAttribute(SecurityRuleSet ruleSet)
+ {
+ RuleSet = ruleSet;
+ }
+
+ // Should fully trusted transparent code skip IL verification
+ public bool SkipVerificationInFullTrust { get; set; }
+
+ public SecurityRuleSet RuleSet { get; }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/SecuritySafeCriticalAttribute.cs b/src/mscorlib/shared/System/Security/SecuritySafeCriticalAttribute.cs
new file mode 100644
index 0000000000..ee2e4b0499
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecuritySafeCriticalAttribute.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ // SecuritySafeCriticalAttribute:
+ // Indicates that the code may contain violations to the security critical rules (e.g. transitions from
+ // critical to non-public transparent, transparent to non-public critical, etc.), has been audited for
+ // security concerns and is considered security clean. Also indicates that the code is considered SecurityCritical.
+ // The effect of this attribute is as if the code was marked [SecurityCritical][SecurityTreatAsSafe].
+ // At assembly-scope, all rule checks will be suppressed within the assembly and for calls made against the assembly.
+ // At type-scope, all rule checks will be suppressed for members within the type and for calls made against the type.
+ // At member level (e.g. field and method) the code will be treated as public - i.e. no rule checks for the members.
+
+ [AttributeUsage(AttributeTargets.Class |
+ AttributeTargets.Struct |
+ AttributeTargets.Enum |
+ AttributeTargets.Constructor |
+ AttributeTargets.Method |
+ AttributeTargets.Field |
+ AttributeTargets.Interface |
+ AttributeTargets.Delegate,
+ AllowMultiple = false,
+ Inherited = false)]
+ public sealed class SecuritySafeCriticalAttribute : Attribute
+ {
+ public SecuritySafeCriticalAttribute() { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Security/SecurityTransparentAttribute.cs b/src/mscorlib/shared/System/Security/SecurityTransparentAttribute.cs
new file mode 100644
index 0000000000..03f41387ae
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecurityTransparentAttribute.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ // SecurityTransparentAttribute:
+ // Indicates the assembly contains only transparent code.
+ // Security critical actions will be restricted or converted into less critical actions. For example,
+ // Assert will be restricted, SuppressUnmanagedCode, LinkDemand, unsafe, and unverifiable code will be converted
+ // into Full-Demands.
+
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
+ public sealed class SecurityTransparentAttribute : Attribute
+ {
+ public SecurityTransparentAttribute() { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/SecurityTreatAsSafeAttribute.cs b/src/mscorlib/shared/System/Security/SecurityTreatAsSafeAttribute.cs
new file mode 100644
index 0000000000..7a95122bf0
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SecurityTreatAsSafeAttribute.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ // SecurityTreatAsSafeAttribute:
+ // Indicates that the code may contain violations to the security critical rules (e.g. transitions from
+ // critical to non-public transparent, transparent to non-public critical, etc.), has been audited for
+ // security concerns and is considered security clean.
+ // At assembly-scope, all rule checks will be suppressed within the assembly and for calls made against the assembly.
+ // At type-scope, all rule checks will be suppressed for members within the type and for calls made against the type.
+ // At member level (e.g. field and method) the code will be treated as public - i.e. no rule checks for the members.
+
+ [AttributeUsage(AttributeTargets.Assembly |
+ AttributeTargets.Class |
+ AttributeTargets.Struct |
+ AttributeTargets.Enum |
+ AttributeTargets.Constructor |
+ AttributeTargets.Method |
+ AttributeTargets.Field |
+ AttributeTargets.Interface |
+ AttributeTargets.Delegate,
+ AllowMultiple = false,
+ Inherited = false)]
+ [Obsolete("SecurityTreatAsSafe is only used for .NET 2.0 transparency compatibility. Please use the SecuritySafeCriticalAttribute instead.")]
+ public sealed class SecurityTreatAsSafeAttribute : Attribute
+ {
+ public SecurityTreatAsSafeAttribute() { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/SuppressUnmanagedCodeSecurityAttribute.cs b/src/mscorlib/shared/System/Security/SuppressUnmanagedCodeSecurityAttribute.cs
new file mode 100644
index 0000000000..a60b8d3668
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/SuppressUnmanagedCodeSecurityAttribute.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ // SuppressUnmanagedCodeSecurityAttribute:
+ // Indicates that the target P/Invoke method(s) should skip the per-call
+ // security checked for unmanaged code permission.
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = true, Inherited = false)]
+ public sealed class SuppressUnmanagedCodeSecurityAttribute : Attribute
+ {
+ public SuppressUnmanagedCodeSecurityAttribute() { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/UnverifiableCodeAttribute.cs b/src/mscorlib/shared/System/Security/UnverifiableCodeAttribute.cs
new file mode 100644
index 0000000000..1560b6617b
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/UnverifiableCodeAttribute.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Security
+{
+ // UnverifiableCodeAttribute:
+ // Indicates that the target module contains unverifiable code.
+ [AttributeUsage(AttributeTargets.Module, AllowMultiple = true, Inherited = false)]
+ public sealed class UnverifiableCodeAttribute : Attribute
+ {
+ public UnverifiableCodeAttribute() { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Security/VerificationException.cs b/src/mscorlib/shared/System/Security/VerificationException.cs
new file mode 100644
index 0000000000..9641e1aa46
--- /dev/null
+++ b/src/mscorlib/shared/System/Security/VerificationException.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Security
+{
+ [Serializable]
+ public class VerificationException : SystemException
+ {
+ public VerificationException()
+ : base(SR.Verification_Exception)
+ {
+ HResult = __HResults.COR_E_VERIFICATION;
+ }
+
+ public VerificationException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_VERIFICATION;
+ }
+
+ public VerificationException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_VERIFICATION;
+ }
+
+ protected VerificationException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/StackOverflowException.cs b/src/mscorlib/shared/System/StackOverflowException.cs
new file mode 100644
index 0000000000..0a875e7373
--- /dev/null
+++ b/src/mscorlib/shared/System/StackOverflowException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: The exception class for stack overflow.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public sealed class StackOverflowException : SystemException
+ {
+ public StackOverflowException()
+ : base(SR.Arg_StackOverflowException)
+ {
+ HResult = __HResults.COR_E_STACKOVERFLOW;
+ }
+
+ public StackOverflowException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_STACKOVERFLOW;
+ }
+
+ public StackOverflowException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_STACKOVERFLOW;
+ }
+
+ internal StackOverflowException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs
new file mode 100644
index 0000000000..b327e770d5
--- /dev/null
+++ b/src/mscorlib/shared/System/StringComparer.cs
@@ -0,0 +1,274 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Diagnostics.Contracts;
+
+namespace System
+{
+ [Serializable]
+ public abstract class StringComparer : IComparer, IEqualityComparer, IComparer<string>, IEqualityComparer<string>
+ {
+ private static readonly CultureAwareComparer s_invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, false);
+ private static readonly CultureAwareComparer s_invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, true);
+ private static readonly OrdinalComparer s_ordinal = new OrdinalComparer();
+ private static readonly OrdinalIgnoreCaseComparer s_ordinalIgnoreCase = new OrdinalIgnoreCaseComparer();
+
+ public static StringComparer InvariantCulture
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<StringComparer>() != null);
+ return s_invariantCulture;
+ }
+ }
+
+ public static StringComparer InvariantCultureIgnoreCase
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<StringComparer>() != null);
+ return s_invariantCultureIgnoreCase;
+ }
+ }
+
+ public static StringComparer CurrentCulture
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<StringComparer>() != null);
+ return new CultureAwareComparer(CultureInfo.CurrentCulture, false);
+ }
+ }
+
+ public static StringComparer CurrentCultureIgnoreCase
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<StringComparer>() != null);
+ return new CultureAwareComparer(CultureInfo.CurrentCulture, true);
+ }
+ }
+
+ public static StringComparer Ordinal
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<StringComparer>() != null);
+ return s_ordinal;
+ }
+ }
+
+ public static StringComparer OrdinalIgnoreCase
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<StringComparer>() != null);
+ return s_ordinalIgnoreCase;
+ }
+ }
+
+ // Convert a StringComparison to a StringComparer
+ public static StringComparer FromComparison(StringComparison comparisonType)
+ {
+ switch (comparisonType)
+ {
+ case StringComparison.CurrentCulture:
+ return CurrentCulture;
+ case StringComparison.CurrentCultureIgnoreCase:
+ return CurrentCultureIgnoreCase;
+ case StringComparison.InvariantCulture:
+ return InvariantCulture;
+ case StringComparison.InvariantCultureIgnoreCase:
+ return InvariantCultureIgnoreCase;
+ case StringComparison.Ordinal:
+ return Ordinal;
+ case StringComparison.OrdinalIgnoreCase:
+ return OrdinalIgnoreCase;
+ default:
+ throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
+ }
+ }
+
+ public static StringComparer Create(CultureInfo culture, bool ignoreCase)
+ {
+ if (culture == null)
+ {
+ throw new ArgumentNullException(nameof(culture));
+ }
+ Contract.Ensures(Contract.Result<StringComparer>() != null);
+ Contract.EndContractBlock();
+
+ return new CultureAwareComparer(culture, ignoreCase);
+ }
+
+ public int Compare(object x, object y)
+ {
+ if (x == y) return 0;
+ if (x == null) return -1;
+ if (y == null) return 1;
+
+ String sa = x as String;
+ if (sa != null)
+ {
+ String sb = y as String;
+ if (sb != null)
+ {
+ return Compare(sa, sb);
+ }
+ }
+
+ IComparable ia = x as IComparable;
+ if (ia != null)
+ {
+ return ia.CompareTo(y);
+ }
+
+ throw new ArgumentException(SR.Argument_ImplementIComparable);
+ }
+
+
+ public new bool Equals(Object x, Object y)
+ {
+ if (x == y) return true;
+ if (x == null || y == null) return false;
+
+ String sa = x as String;
+ if (sa != null)
+ {
+ String sb = y as String;
+ if (sb != null)
+ {
+ return Equals(sa, sb);
+ }
+ }
+ return x.Equals(y);
+ }
+
+ public int GetHashCode(object obj)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+ Contract.EndContractBlock();
+
+ string s = obj as string;
+ if (s != null)
+ {
+ return GetHashCode(s);
+ }
+ return obj.GetHashCode();
+ }
+
+ public abstract int Compare(String x, String y);
+ public abstract bool Equals(String x, String y);
+ public abstract int GetHashCode(string obj);
+ }
+
+ [Serializable]
+ internal sealed class CultureAwareComparer : StringComparer
+ {
+ private readonly CompareInfo _compareInfo;
+ private readonly CompareOptions _options;
+
+ internal CultureAwareComparer(CultureInfo culture, bool ignoreCase)
+ {
+ _compareInfo = culture.CompareInfo;
+ _options = ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None;
+ }
+
+ public override int Compare(string x, string y)
+ {
+ if (object.ReferenceEquals(x, y)) return 0;
+ if (x == null) return -1;
+ if (y == null) return 1;
+ return _compareInfo.Compare(x, y, _options);
+ }
+
+ public override bool Equals(string x, string y)
+ {
+ if (object.ReferenceEquals(x, y)) return true;
+ if (x == null || y == null) return false;
+ return _compareInfo.Compare(x, y, _options) == 0;
+ }
+
+ public override int GetHashCode(string obj)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+ return _compareInfo.GetHashCodeOfString(obj, _options);
+ }
+
+ // Equals method for the comparer itself.
+ public override bool Equals(object obj)
+ {
+ CultureAwareComparer comparer = obj as CultureAwareComparer;
+ return
+ comparer != null &&
+ _options == comparer._options &&
+ _compareInfo.Equals(comparer._compareInfo);
+ }
+
+ public override int GetHashCode()
+ {
+ int hashCode = _compareInfo.GetHashCode();
+ return _options == CompareOptions.None ? hashCode : ~hashCode;
+ }
+ }
+
+ [Serializable]
+ internal sealed class OrdinalComparer : StringComparer
+ {
+ public override int Compare(string x, string y) => string.CompareOrdinal(x, y);
+
+ public override bool Equals(string x, string y) => string.Equals(x, y);
+
+ public override int GetHashCode(string obj)
+ {
+ if (obj == null)
+ {
+#if CORECLR
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj);
+#else
+ throw new ArgumentNullException(nameof(obj));
+#endif
+ }
+ return obj.GetHashCode();
+ }
+
+ // Equals/GetHashCode methods for the comparer itself.
+ public override bool Equals(object obj) => obj is OrdinalComparer;
+ public override int GetHashCode() => nameof(OrdinalComparer).GetHashCode();
+ }
+
+ [Serializable]
+ internal sealed class OrdinalIgnoreCaseComparer : StringComparer
+ {
+ public override int Compare(string x, string y) => string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
+
+ public override bool Equals(string x, string y) => string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
+
+ public override int GetHashCode(string obj)
+ {
+ if (obj == null)
+ {
+#if CORECLR
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj);
+#else
+ throw new ArgumentNullException(nameof(obj));
+#endif
+ }
+ return TextInfo.GetHashCodeOrdinalIgnoreCase(obj);
+ }
+
+ // Equals/GetHashCode methods for the comparer itself.
+ public override bool Equals(object obj) => obj is OrdinalIgnoreCaseComparer;
+ public override int GetHashCode() => nameof(OrdinalIgnoreCaseComparer).GetHashCode();
+ }
+}
diff --git a/src/mscorlib/shared/System/StringComparison.cs b/src/mscorlib/shared/System/StringComparison.cs
new file mode 100644
index 0000000000..8b4e2ae2fe
--- /dev/null
+++ b/src/mscorlib/shared/System/StringComparison.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ [Serializable]
+ public enum StringComparison
+ {
+ CurrentCulture = 0,
+ CurrentCultureIgnoreCase = 1,
+ InvariantCulture = 2,
+ InvariantCultureIgnoreCase = 3,
+ Ordinal = 4,
+ OrdinalIgnoreCase = 5,
+ }
+}
diff --git a/src/mscorlib/shared/System/StringSplitOptions.cs b/src/mscorlib/shared/System/StringSplitOptions.cs
new file mode 100644
index 0000000000..d7020559a1
--- /dev/null
+++ b/src/mscorlib/shared/System/StringSplitOptions.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ [Flags]
+ public enum StringSplitOptions
+ {
+ None = 0,
+ RemoveEmptyEntries = 1
+ }
+}
diff --git a/src/mscorlib/shared/System/SystemException.cs b/src/mscorlib/shared/System/SystemException.cs
new file mode 100644
index 0000000000..f4779a2cd4
--- /dev/null
+++ b/src/mscorlib/shared/System/SystemException.cs
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class SystemException : Exception
+ {
+ public SystemException()
+ : base(SR.Arg_SystemException)
+ {
+ HResult = __HResults.COR_E_SYSTEM;
+ }
+
+ public SystemException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_SYSTEM;
+ }
+
+ public SystemException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_SYSTEM;
+ }
+
+ protected SystemException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/ASCIIEncoding.cs b/src/mscorlib/shared/System/Text/ASCIIEncoding.cs
new file mode 100644
index 0000000000..e5c1194849
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/ASCIIEncoding.cs
@@ -0,0 +1,973 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.Serialization;
+
+namespace System.Text
+{
+ // ASCIIEncoding
+ //
+ // Note that ASCIIEncoding is optimized with no best fit and ? for fallback.
+ // It doesn't come in other flavors.
+ //
+ // Note: ASCIIEncoding is the only encoding that doesn't do best fit (windows has best fit).
+ //
+ // Note: IsAlwaysNormalized remains false because 1/2 the code points are unassigned, so they'd
+ // use fallbacks, and we cannot guarantee that fallbacks are normalized.
+
+ [Serializable]
+ public class ASCIIEncoding : Encoding
+ {
+ // Allow for devirtualization (see https://github.com/dotnet/coreclr/pull/9230)
+ [Serializable]
+ internal sealed class ASCIIEncodingSealed : ASCIIEncoding { }
+
+ // Used by Encoding.ASCII for lazy initialization
+ // The initialization code will not be run until a static member of the class is referenced
+ internal static readonly ASCIIEncodingSealed s_default = new ASCIIEncodingSealed();
+
+ public ASCIIEncoding() : base(Encoding.CodePageASCII)
+ {
+ }
+
+ internal override void SetDefaultFallbacks()
+ {
+ // For ASCIIEncoding we just use default replacement fallback
+ this.encoderFallback = EncoderFallback.ReplacementFallback;
+ this.decoderFallback = DecoderFallback.ReplacementFallback;
+ }
+
+ // WARNING: GetByteCount(string chars), GetBytes(string chars,...), and GetString(byte[] byteIndex...)
+ // WARNING: have different variable names than EncodingNLS.cs, so this can't just be cut & pasted,
+ // WARNING: or it'll break VB's way of calling these.
+ //
+ // The following methods are copied from EncodingNLS.cs.
+ // Unfortunately EncodingNLS.cs is internal and we're public, so we have to re-implement them here.
+ // These should be kept in sync for the following classes:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ // Returns the number of bytes required to encode a range of characters in
+ // a character array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetByteCount(char[] chars, int index, int count)
+ {
+ // Validate input parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - index < count)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input, return 0, avoid fixed empty array problem
+ if (count == 0)
+ return 0;
+
+ // Just call the pointer version
+ fixed (char* pChars = chars)
+ return GetByteCount(pChars + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetByteCount(String chars)
+ {
+ // Validate input
+ if (chars==null)
+ throw new ArgumentNullException("chars");
+ Contract.EndContractBlock();
+
+ fixed (char* pChars = chars)
+ return GetByteCount(pChars, chars.Length, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetByteCount(char* chars, int count)
+ {
+ // Validate Parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Call it with empty encoder
+ return GetByteCount(chars, count, null);
+ }
+
+ // Parent method is safe.
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ public override unsafe int GetBytes(String chars, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ if (chars == null || bytes == null)
+ throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCount);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like empty byte arrays
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0])
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // Encodes a range of characters in a character array into a range of bytes
+ // in a byte array. An exception occurs if the byte array is not large
+ // enough to hold the complete encoding of the characters. The
+ // GetByteCount method can be used to determine the exact number of
+ // bytes that will be produced for a given range of characters.
+ // Alternatively, the GetMaxByteCount method can be used to
+ // determine the maximum number of bytes that will be produced for a given
+ // number of characters, regardless of the actual character values.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ // Validate parameters
+ if (chars == null || bytes == null)
+ throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If nothing to encode return 0, avoid fixed problem
+ if (charCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like empty byte arrays
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0])
+ // Remember that byteCount is # to decode, not size of array.
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetBytes(chars, charCount, bytes, byteCount, null);
+ }
+
+ // Returns the number of characters produced by decoding a range of bytes
+ // in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetCharCount(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - index < count)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input just return 0, fixed doesn't like 0 length arrays
+ if (count == 0)
+ return 0;
+
+ // Just call pointer version
+ fixed (byte* pBytes = bytes)
+ return GetCharCount(pBytes + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetCharCount(byte* bytes, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetCharCount(bytes, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
+ char[] chars, int charIndex)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (byteIndex < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if ( bytes.Length - byteIndex < byteCount)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (charIndex < 0 || charIndex > chars.Length)
+ throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If no input, return 0 & avoid fixed problem
+ if (byteCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int charCount = chars.Length - charIndex;
+
+ // Fixed doesn't like empty char arrays
+ if (chars.Length == 0)
+ chars = new char[1];
+
+ fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0])
+ // Remember that charCount is # to decode, not size of array
+ return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetChars(bytes, byteCount, chars, charCount, null);
+ }
+
+ // Returns a string containing the decoded representation of a range of
+ // bytes in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe String GetString(byte[] bytes, int byteIndex, int byteCount)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (byteIndex < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+
+ if (bytes.Length - byteIndex < byteCount)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // Avoid problems with empty input buffer
+ if (byteCount == 0) return String.Empty;
+
+ fixed (byte* pBytes = bytes)
+ return String.CreateStringFromEncoding(
+ pBytes + byteIndex, byteCount, this);
+ }
+
+ //
+ // End of standard methods copied from EncodingNLS.cs
+ //
+
+ // GetByteCount
+ // Note: We start by assuming that the output will be the same as count. Having
+ // an encoder or fallback may change that assumption
+ internal override unsafe int GetByteCount(char* chars, int charCount, EncoderNLS encoder)
+ {
+ // Just need to ASSERT, this is called by something else internal that checked parameters already
+ Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetByteCount]count is negative");
+ Debug.Assert(chars != null, "[ASCIIEncoding.GetByteCount]chars is null");
+
+ // Assert because we shouldn't be able to have a null encoder.
+ Debug.Assert(encoderFallback != null, "[ASCIIEncoding.GetByteCount]Attempting to use null fallback encoder");
+
+ char charLeftOver = (char)0;
+ EncoderReplacementFallback fallback = null;
+
+ // Start by assuming default count, then +/- for fallback characters
+ char* charEnd = chars + charCount;
+
+ // For fallback we may need a fallback buffer, we know we aren't default fallback.
+ EncoderFallbackBuffer fallbackBuffer = null;
+ char* charsForFallback;
+
+ if (encoder != null)
+ {
+ charLeftOver = encoder.charLeftOver;
+ Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver),
+ "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate");
+
+ fallback = encoder.Fallback as EncoderReplacementFallback;
+
+ // We mustn't have left over fallback data when counting
+ if (encoder.InternalHasFallbackBuffer)
+ {
+ // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary
+ fallbackBuffer = encoder.FallbackBuffer;
+ if (fallbackBuffer.Remaining > 0 && encoder.m_throwOnOverflow)
+ throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(chars, charEnd, encoder, false);
+ }
+
+ // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert
+ Debug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer ||
+ encoder.FallbackBuffer.Remaining == 0,
+ "[ASCIICodePageEncoding.GetByteCount]Expected empty fallback buffer");
+ }
+ else
+ {
+ fallback = this.EncoderFallback as EncoderReplacementFallback;
+ }
+
+ // If we have an encoder AND we aren't using default fallback,
+ // then we may have a complicated count.
+ if (fallback != null && fallback.MaxCharCount == 1)
+ {
+ // Replacement fallback encodes surrogate pairs as two ?? (or two whatever), so return size is always
+ // same as input size.
+ // Note that no existing SBCS code pages map code points to supplimentary characters, so this is easy.
+
+ // We could however have 1 extra byte if the last call had an encoder and a funky fallback and
+ // if we don't use the funky fallback this time.
+
+ // Do we have an extra char left over from last time?
+ if (charLeftOver > 0)
+ charCount++;
+
+ return (charCount);
+ }
+
+ // Count is more complicated if you have a funky fallback
+ // For fallback we may need a fallback buffer, we know we're not default fallback
+ int byteCount = 0;
+
+ // We may have a left over character from last time, try and process it.
+ if (charLeftOver > 0)
+ {
+ Debug.Assert(Char.IsHighSurrogate(charLeftOver), "[ASCIIEncoding.GetByteCount]leftover character should be high surrogate");
+ Debug.Assert(encoder != null, "[ASCIIEncoding.GetByteCount]Expected encoder");
+
+ // Since left over char was a surrogate, it'll have to be fallen back.
+ // Get Fallback
+ fallbackBuffer = encoder.FallbackBuffer;
+ fallbackBuffer.InternalInitialize(chars, charEnd, encoder, false);
+
+ // This will fallback a pair if *chars is a low surrogate
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
+ chars = charsForFallback;
+ }
+
+ // Now we may have fallback char[] already from the encoder
+
+ // Go ahead and do it, including the fallback.
+ char ch;
+ while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 ||
+ chars < charEnd)
+ {
+ // First unwind any fallback
+ if (ch == 0)
+ {
+ // No fallback, just get next char
+ ch = *chars;
+ chars++;
+ }
+
+ // Check for fallback, this'll catch surrogate pairs too.
+ // no chars >= 0x80 are allowed.
+ if (ch > 0x7f)
+ {
+ if (fallbackBuffer == null)
+ {
+ // Initialize the buffer
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+ fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, false);
+ }
+
+ // Get Fallback
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(ch, ref charsForFallback);
+ chars = charsForFallback;
+ continue;
+ }
+
+ // We'll use this one
+ byteCount++;
+ }
+
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
+ "[ASCIIEncoding.GetByteCount]Expected Empty fallback buffer");
+
+ return byteCount;
+ }
+
+ internal override unsafe int GetBytes(char* chars, int charCount,
+ byte* bytes, int byteCount, EncoderNLS encoder)
+ {
+ // Just need to ASSERT, this is called by something else internal that checked parameters already
+ Debug.Assert(bytes != null, "[ASCIIEncoding.GetBytes]bytes is null");
+ Debug.Assert(byteCount >= 0, "[ASCIIEncoding.GetBytes]byteCount is negative");
+ Debug.Assert(chars != null, "[ASCIIEncoding.GetBytes]chars is null");
+ Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetBytes]charCount is negative");
+
+ // Assert because we shouldn't be able to have a null encoder.
+ Debug.Assert(encoderFallback != null, "[ASCIIEncoding.GetBytes]Attempting to use null encoder fallback");
+
+ // Get any left over characters
+ char charLeftOver = (char)0;
+ EncoderReplacementFallback fallback = null;
+
+ // For fallback we may need a fallback buffer, we know we aren't default fallback.
+ EncoderFallbackBuffer fallbackBuffer = null;
+ char* charsForFallback;
+
+ // prepare our end
+ char* charEnd = chars + charCount;
+ byte* byteStart = bytes;
+ char* charStart = chars;
+
+ if (encoder != null)
+ {
+ charLeftOver = encoder.charLeftOver;
+ fallback = encoder.Fallback as EncoderReplacementFallback;
+
+ // We mustn't have left over fallback data when counting
+ if (encoder.InternalHasFallbackBuffer)
+ {
+ // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary
+ fallbackBuffer = encoder.FallbackBuffer;
+ if (fallbackBuffer.Remaining > 0 && encoder.m_throwOnOverflow)
+ throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
+ }
+
+ Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver),
+ "[ASCIIEncoding.GetBytes]leftover character should be high surrogate");
+
+ // Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert
+ Debug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer ||
+ encoder.FallbackBuffer.Remaining == 0,
+ "[ASCIICodePageEncoding.GetBytes]Expected empty fallback buffer");
+ }
+ else
+ {
+ fallback = this.EncoderFallback as EncoderReplacementFallback;
+ }
+
+
+ // See if we do the fast default or slightly slower fallback
+ if (fallback != null && fallback.MaxCharCount == 1)
+ {
+ // Fast version
+ char cReplacement = fallback.DefaultString[0];
+
+ // Check for replacements in range, otherwise fall back to slow version.
+ if (cReplacement <= (char)0x7f)
+ {
+ // We should have exactly as many output bytes as input bytes, unless there's a left
+ // over character, in which case we may need one more.
+ // If we had a left over character will have to add a ? (This happens if they had a funky
+ // fallback last time, but not this time.) (We can't spit any out though
+ // because with fallback encoder each surrogate is treated as a seperate code point)
+ if (charLeftOver > 0)
+ {
+ // Have to have room
+ // Throw even if doing no throw version because this is just 1 char,
+ // so buffer will never be big enough
+ if (byteCount == 0)
+ ThrowBytesOverflow(encoder, true);
+
+ // This'll make sure we still have more room and also make sure our return value is correct.
+ *(bytes++) = (byte)cReplacement;
+ byteCount--; // We used one of the ones we were counting.
+ }
+
+ // This keeps us from overrunning our output buffer
+ if (byteCount < charCount)
+ {
+ // Throw or make buffer smaller?
+ ThrowBytesOverflow(encoder, byteCount < 1);
+
+ // Just use what we can
+ charEnd = chars + byteCount;
+ }
+
+ // We just do a quick copy
+ while (chars < charEnd)
+ {
+ char ch2 = *(chars++);
+ if (ch2 >= 0x0080) *(bytes++) = (byte)cReplacement;
+ else *(bytes++) = unchecked((byte)(ch2));
+ }
+
+ // Clear encoder
+ if (encoder != null)
+ {
+ encoder.charLeftOver = (char)0;
+ encoder.m_charsUsed = (int)(chars - charStart);
+ }
+
+ return (int)(bytes - byteStart);
+ }
+ }
+
+ // Slower version, have to do real fallback.
+
+ // prepare our end
+ byte* byteEnd = bytes + byteCount;
+
+ // We may have a left over character from last time, try and process it.
+ if (charLeftOver > 0)
+ {
+ // Initialize the buffer
+ Debug.Assert(encoder != null,
+ "[ASCIIEncoding.GetBytes]Expected non null encoder if we have surrogate left over");
+ fallbackBuffer = encoder.FallbackBuffer;
+ fallbackBuffer.InternalInitialize(chars, charEnd, encoder, true);
+
+ // Since left over char was a surrogate, it'll have to be fallen back.
+ // Get Fallback
+ // This will fallback a pair if *chars is a low surrogate
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
+ chars = charsForFallback;
+ }
+
+ // Now we may have fallback char[] already from the encoder
+
+ // Go ahead and do it, including the fallback.
+ char ch;
+ while ((ch = (fallbackBuffer == null) ? '\0' : fallbackBuffer.InternalGetNextChar()) != 0 ||
+ chars < charEnd)
+ {
+ // First unwind any fallback
+ if (ch == 0)
+ {
+ // No fallback, just get next char
+ ch = *chars;
+ chars++;
+ }
+
+ // Check for fallback, this'll catch surrogate pairs too.
+ // All characters >= 0x80 must fall back.
+ if (ch > 0x7f)
+ {
+ // Initialize the buffer
+ if (fallbackBuffer == null)
+ {
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+ fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, true);
+ }
+
+ // Get Fallback
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(ch, ref charsForFallback);
+ chars = charsForFallback;
+
+ // Go ahead & continue (& do the fallback)
+ continue;
+ }
+
+ // We'll use this one
+ // Bounds check
+ if (bytes >= byteEnd)
+ {
+ // didn't use this char, we'll throw or use buffer
+ if (fallbackBuffer == null || fallbackBuffer.bFallingBack == false)
+ {
+ Debug.Assert(chars > charStart || bytes == byteStart,
+ "[ASCIIEncoding.GetBytes]Expected chars to have advanced already.");
+ chars--; // don't use last char
+ }
+ else
+ fallbackBuffer.MovePrevious();
+
+ // Are we throwing or using buffer?
+ ThrowBytesOverflow(encoder, bytes == byteStart); // throw?
+ break; // don't throw, stop
+ }
+
+ // Go ahead and add it
+ *bytes = unchecked((byte)ch);
+ bytes++;
+ }
+
+ // Need to do encoder stuff
+ if (encoder != null)
+ {
+ // Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases
+ if (fallbackBuffer != null && !fallbackBuffer.bUsedEncoder)
+ // Clear it in case of MustFlush
+ encoder.charLeftOver = (char)0;
+
+ // Set our chars used count
+ encoder.m_charsUsed = (int)(chars - charStart);
+ }
+
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 ||
+ (encoder != null && !encoder.m_throwOnOverflow),
+ "[ASCIIEncoding.GetBytes]Expected Empty fallback buffer at end");
+
+ return (int)(bytes - byteStart);
+ }
+
+ // This is internal and called by something else,
+ internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS decoder)
+ {
+ // Just assert, we're called internally so these should be safe, checked already
+ Debug.Assert(bytes != null, "[ASCIIEncoding.GetCharCount]bytes is null");
+ Debug.Assert(count >= 0, "[ASCIIEncoding.GetCharCount]byteCount is negative");
+
+ // ASCII doesn't do best fit, so don't have to check for it, find out which decoder fallback we're using
+ DecoderReplacementFallback fallback = null;
+
+ if (decoder == null)
+ fallback = this.DecoderFallback as DecoderReplacementFallback;
+ else
+ {
+ fallback = decoder.Fallback as DecoderReplacementFallback;
+ Debug.Assert(!decoder.m_throwOnOverflow || !decoder.InternalHasFallbackBuffer ||
+ decoder.FallbackBuffer.Remaining == 0,
+ "[ASCIICodePageEncoding.GetCharCount]Expected empty fallback buffer");
+ }
+
+ if (fallback != null && fallback.MaxCharCount == 1)
+ {
+ // Just return length, SBCS stay the same length because they don't map to surrogate
+ // pairs and we don't have a decoder fallback.
+
+ return count;
+ }
+
+ // Only need decoder fallback buffer if not using default replacement fallback, no best fit for ASCII
+ DecoderFallbackBuffer fallbackBuffer = null;
+
+ // Have to do it the hard way.
+ // Assume charCount will be == count
+ int charCount = count;
+ byte[] byteBuffer = new byte[1];
+
+ // Do it our fast way
+ byte* byteEnd = bytes + count;
+
+ // Quick loop
+ while (bytes < byteEnd)
+ {
+ // Faster if don't use *bytes++;
+ byte b = *bytes;
+ bytes++;
+
+ // If unknown we have to do fallback count
+ if (b >= 0x80)
+ {
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+ fallbackBuffer.InternalInitialize(byteEnd - count, null);
+ }
+
+ // Use fallback buffer
+ byteBuffer[0] = b;
+ charCount--; // Have to unreserve the one we already allocated for b
+ charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
+ }
+ }
+
+ // Fallback buffer must be empty
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
+ "[ASCIIEncoding.GetCharCount]Expected Empty fallback buffer");
+
+ // Converted sequence is same length as input
+ return charCount;
+ }
+
+ internal override unsafe int GetChars(byte* bytes, int byteCount,
+ char* chars, int charCount, DecoderNLS decoder)
+ {
+ // Just need to ASSERT, this is called by something else internal that checked parameters already
+ Debug.Assert(bytes != null, "[ASCIIEncoding.GetChars]bytes is null");
+ Debug.Assert(byteCount >= 0, "[ASCIIEncoding.GetChars]byteCount is negative");
+ Debug.Assert(chars != null, "[ASCIIEncoding.GetChars]chars is null");
+ Debug.Assert(charCount >= 0, "[ASCIIEncoding.GetChars]charCount is negative");
+
+ // Do it fast way if using ? replacement fallback
+ byte* byteEnd = bytes + byteCount;
+ byte* byteStart = bytes;
+ char* charStart = chars;
+
+ // Note: ASCII doesn't do best fit, but we have to fallback if they use something > 0x7f
+ // Only need decoder fallback buffer if not using ? fallback.
+ // ASCII doesn't do best fit, so don't have to check for it, find out which decoder fallback we're using
+ DecoderReplacementFallback fallback = null;
+ char* charsForFallback;
+
+ if (decoder == null)
+ fallback = this.DecoderFallback as DecoderReplacementFallback;
+ else
+ {
+ fallback = decoder.Fallback as DecoderReplacementFallback;
+ Debug.Assert(!decoder.m_throwOnOverflow || !decoder.InternalHasFallbackBuffer ||
+ decoder.FallbackBuffer.Remaining == 0,
+ "[ASCIICodePageEncoding.GetChars]Expected empty fallback buffer");
+ }
+
+ if (fallback != null && fallback.MaxCharCount == 1)
+ {
+ // Try it the fast way
+ char replacementChar = fallback.DefaultString[0];
+
+ // Need byteCount chars, otherwise too small buffer
+ if (charCount < byteCount)
+ {
+ // Need at least 1 output byte, throw if must throw
+ ThrowCharsOverflow(decoder, charCount < 1);
+
+ // Not throwing, use what we can
+ byteEnd = bytes + charCount;
+ }
+
+ // Quick loop, just do '?' replacement because we don't have fallbacks for decodings.
+ while (bytes < byteEnd)
+ {
+ byte b = *(bytes++);
+ if (b >= 0x80)
+ // This is an invalid byte in the ASCII encoding.
+ *(chars++) = replacementChar;
+ else
+ *(chars++) = unchecked((char)b);
+ }
+
+ // bytes & chars used are the same
+ if (decoder != null)
+ decoder.m_bytesUsed = (int)(bytes - byteStart);
+ return (int)(chars - charStart);
+ }
+
+ // Slower way's going to need a fallback buffer
+ DecoderFallbackBuffer fallbackBuffer = null;
+ byte[] byteBuffer = new byte[1];
+ char* charEnd = chars + charCount;
+
+ // Not quite so fast loop
+ while (bytes < byteEnd)
+ {
+ // Faster if don't use *bytes++;
+ byte b = *(bytes);
+ bytes++;
+
+ if (b >= 0x80)
+ {
+ // This is an invalid byte in the ASCII encoding.
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.DecoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+ fallbackBuffer.InternalInitialize(byteEnd - byteCount, charEnd);
+ }
+
+ // Use fallback buffer
+ byteBuffer[0] = b;
+
+ // Note that chars won't get updated unless this succeeds
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
+ chars = charsForFallback;
+
+ if (!fallbackResult)
+ {
+ // May or may not throw, but we didn't get this byte
+ Debug.Assert(bytes > byteStart || chars == charStart,
+ "[ASCIIEncoding.GetChars]Expected bytes to have advanced already (fallback case)");
+ bytes--; // unused byte
+ fallbackBuffer.InternalReset(); // Didn't fall this back
+ ThrowCharsOverflow(decoder, chars == charStart); // throw?
+ break; // don't throw, but stop loop
+ }
+ }
+ else
+ {
+ // Make sure we have buffer space
+ if (chars >= charEnd)
+ {
+ Debug.Assert(bytes > byteStart || chars == charStart,
+ "[ASCIIEncoding.GetChars]Expected bytes to have advanced already (normal case)");
+ bytes--; // unused byte
+ ThrowCharsOverflow(decoder, chars == charStart); // throw?
+ break; // don't throw, but stop loop
+ }
+
+ *(chars) = unchecked((char)b);
+ chars++;
+ }
+ }
+
+ // Might have had decoder fallback stuff.
+ if (decoder != null)
+ decoder.m_bytesUsed = (int)(bytes - byteStart);
+
+ // Expect Empty fallback buffer for GetChars
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
+ "[ASCIIEncoding.GetChars]Expected Empty fallback buffer");
+
+ return (int)(chars - charStart);
+ }
+
+
+ public override int GetMaxByteCount(int charCount)
+ {
+ if (charCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(charCount),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Characters would be # of characters + 1 in case high surrogate is ? * max fallback
+ long byteCount = (long)charCount + 1;
+
+ if (EncoderFallback.MaxCharCount > 1)
+ byteCount *= EncoderFallback.MaxCharCount;
+
+ // 1 to 1 for most characters. Only surrogates with fallbacks have less.
+
+ if (byteCount > 0x7fffffff)
+ throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
+ return (int)byteCount;
+ }
+
+
+ public override int GetMaxCharCount(int byteCount)
+ {
+ if (byteCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(byteCount),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Just return length, SBCS stay the same length because they don't map to surrogate
+ long charCount = (long)byteCount;
+
+ // 1 to 1 for most characters. Only surrogates with fallbacks have less, unknown fallbacks could be longer.
+ if (DecoderFallback.MaxCharCount > 1)
+ charCount *= DecoderFallback.MaxCharCount;
+
+ if (charCount > 0x7fffffff)
+ throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow);
+
+ return (int)charCount;
+ }
+
+ // True if and only if the encoding only uses single byte code points. (Ie, ASCII, 1252, etc)
+
+ public override bool IsSingleByte
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override Decoder GetDecoder()
+ {
+ return new DecoderNLS(this);
+ }
+
+
+ public override Encoder GetEncoder()
+ {
+ return new EncoderNLS(this);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/Decoder.cs b/src/mscorlib/shared/System/Text/Decoder.cs
new file mode 100644
index 0000000000..b2a003037b
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/Decoder.cs
@@ -0,0 +1,339 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+using System.Text;
+using System;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+
+namespace System.Text
+{
+ // A Decoder is used to decode a sequence of blocks of bytes into a
+ // sequence of blocks of characters. Following instantiation of a decoder,
+ // sequential blocks of bytes are converted into blocks of characters through
+ // calls to the GetChars method. The decoder maintains state between the
+ // conversions, allowing it to correctly decode byte sequences that span
+ // adjacent blocks.
+ //
+ // Instances of specific implementations of the Decoder abstract base
+ // class are typically obtained through calls to the GetDecoder method
+ // of Encoding objects.
+ //
+ [Serializable]
+ public abstract class Decoder
+ {
+ internal DecoderFallback m_fallback = null;
+
+ [NonSerialized]
+ internal DecoderFallbackBuffer m_fallbackBuffer = null;
+
+ internal void SerializeDecoder(SerializationInfo info)
+ {
+ info.AddValue("m_fallback", this.m_fallback);
+ }
+
+ protected Decoder()
+ {
+ // We don't call default reset because default reset probably isn't good if we aren't initialized.
+ }
+
+ public DecoderFallback Fallback
+ {
+ get
+ {
+ return m_fallback;
+ }
+
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+ Contract.EndContractBlock();
+
+ // Can't change fallback if buffer is wrong
+ if (m_fallbackBuffer != null && m_fallbackBuffer.Remaining > 0)
+ throw new ArgumentException(
+ SR.Argument_FallbackBufferNotEmpty, nameof(value));
+
+ m_fallback = value;
+ m_fallbackBuffer = null;
+ }
+ }
+
+ // Note: we don't test for threading here because async access to Encoders and Decoders
+ // doesn't work anyway.
+ public DecoderFallbackBuffer FallbackBuffer
+ {
+ get
+ {
+ if (m_fallbackBuffer == null)
+ {
+ if (m_fallback != null)
+ m_fallbackBuffer = m_fallback.CreateFallbackBuffer();
+ else
+ m_fallbackBuffer = DecoderFallback.ReplacementFallback.CreateFallbackBuffer();
+ }
+
+ return m_fallbackBuffer;
+ }
+ }
+
+ internal bool InternalHasFallbackBuffer
+ {
+ get
+ {
+ return m_fallbackBuffer != null;
+ }
+ }
+
+ // Reset the Decoder
+ //
+ // Normally if we call GetChars() and an error is thrown we don't change the state of the Decoder. This
+ // would allow the caller to correct the error condition and try again (such as if they need a bigger buffer.)
+ //
+ // If the caller doesn't want to try again after GetChars() throws an error, then they need to call Reset().
+ //
+ // Virtual implementation has to call GetChars with flush and a big enough buffer to clear a 0 byte string
+ // We avoid GetMaxCharCount() because a) we can't call the base encoder and b) it might be really big.
+ public virtual void Reset()
+ {
+ byte[] byteTemp = Array.Empty<byte>();
+ char[] charTemp = new char[GetCharCount(byteTemp, 0, 0, true)];
+ GetChars(byteTemp, 0, 0, charTemp, 0, true);
+ if (m_fallbackBuffer != null)
+ m_fallbackBuffer.Reset();
+ }
+
+ // Returns the number of characters the next call to GetChars will
+ // produce if presented with the given range of bytes. The returned value
+ // takes into account the state in which the decoder was left following the
+ // last call to GetChars. The state of the decoder is not affected
+ // by a call to this method.
+ //
+ public abstract int GetCharCount(byte[] bytes, int index, int count);
+
+ public virtual int GetCharCount(byte[] bytes, int index, int count, bool flush)
+ {
+ return GetCharCount(bytes, index, count);
+ }
+
+ // We expect this to be the workhorse for NLS Encodings, but for existing
+ // ones we need a working (if slow) default implementation)
+ [CLSCompliant(false)]
+ public virtual unsafe int GetCharCount(byte* bytes, int count, bool flush)
+ {
+ // Validate input parameters
+ if (bytes == null)
+ throw new ArgumentNullException(nameof(bytes),
+ SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ byte[] arrbyte = new byte[count];
+ int index;
+
+ for (index = 0; index < count; index++)
+ arrbyte[index] = bytes[index];
+
+ return GetCharCount(arrbyte, 0, count);
+ }
+
+ // Decodes a range of bytes in a byte array into a range of characters
+ // in a character array. The method decodes byteCount bytes from
+ // bytes starting at index byteIndex, storing the resulting
+ // characters in chars starting at index charIndex. The
+ // decoding takes into account the state in which the decoder was left
+ // following the last call to this method.
+ //
+ // An exception occurs if the character array is not large enough to
+ // hold the complete decoding of the bytes. The GetCharCount method
+ // can be used to determine the exact number of characters that will be
+ // produced for a given range of bytes. Alternatively, the
+ // GetMaxCharCount method of the Encoding that produced this
+ // decoder can be used to determine the maximum number of characters that
+ // will be produced for a given number of bytes, regardless of the actual
+ // byte values.
+ //
+ public abstract int GetChars(byte[] bytes, int byteIndex, int byteCount,
+ char[] chars, int charIndex);
+
+ public virtual int GetChars(byte[] bytes, int byteIndex, int byteCount,
+ char[] chars, int charIndex, bool flush)
+ {
+ return GetChars(bytes, byteIndex, byteCount, chars, charIndex);
+ }
+
+ // We expect this to be the workhorse for NLS Encodings, but for existing
+ // ones we need a working (if slow) default implementation)
+ //
+ // WARNING WARNING WARNING
+ //
+ // WARNING: If this breaks it could be a security threat. Obviously we
+ // call this internally, so you need to make sure that your pointers, counts
+ // and indexes are correct when you call this method.
+ //
+ // In addition, we have internal code, which will be marked as "safe" calling
+ // this code. However this code is dependent upon the implementation of an
+ // external GetChars() method, which could be overridden by a third party and
+ // the results of which cannot be guaranteed. We use that result to copy
+ // the char[] to our char* output buffer. If the result count was wrong, we
+ // could easily overflow our output buffer. Therefore we do an extra test
+ // when we copy the buffer so that we don't overflow charCount either.
+ [CLSCompliant(false)]
+ public virtual unsafe int GetChars(byte* bytes, int byteCount,
+ char* chars, int charCount, bool flush)
+ {
+ // Validate input parameters
+ if (chars == null || bytes == null)
+ throw new ArgumentNullException(chars == null ? nameof(chars) : nameof(bytes),
+ SR.ArgumentNull_Array);
+
+ if (byteCount < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Get the byte array to convert
+ byte[] arrByte = new byte[byteCount];
+
+ int index;
+ for (index = 0; index < byteCount; index++)
+ arrByte[index] = bytes[index];
+
+ // Get the char array to fill
+ char[] arrChar = new char[charCount];
+
+ // Do the work
+ int result = GetChars(arrByte, 0, byteCount, arrChar, 0, flush);
+
+ Debug.Assert(result <= charCount, "Returned more chars than we have space for");
+
+ // Copy the char array
+ // WARNING: We MUST make sure that we don't copy too many chars. We can't
+ // rely on result because it could be a 3rd party implementation. We need
+ // to make sure we never copy more than charCount chars no matter the value
+ // of result
+ if (result < charCount)
+ charCount = result;
+
+ // We check both result and charCount so that we don't accidentally overrun
+ // our pointer buffer just because of an issue in GetChars
+ for (index = 0; index < charCount; index++)
+ chars[index] = arrChar[index];
+
+ return charCount;
+ }
+
+ // This method is used when the output buffer might not be large enough.
+ // It will decode until it runs out of bytes, and then it will return
+ // true if it the entire input was converted. In either case it
+ // will also return the number of converted bytes and output characters used.
+ // It will only throw a buffer overflow exception if the entire lenght of chars[] is
+ // too small to store the next char. (like 0 or maybe 1 or 4 for some encodings)
+ // We're done processing this buffer only if completed returns true.
+ //
+ // Might consider checking Max...Count to avoid the extra counting step.
+ //
+ // Note that if all of the input bytes are not consumed, then we'll do a /2, which means
+ // that its likely that we didn't consume as many bytes as we could have. For some
+ // applications this could be slow. (Like trying to exactly fill an output buffer from a bigger stream)
+ public virtual void Convert(byte[] bytes, int byteIndex, int byteCount,
+ char[] chars, int charIndex, int charCount, bool flush,
+ out int bytesUsed, out int charsUsed, out bool completed)
+ {
+ // Validate parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException((bytes == null ? nameof(bytes) : nameof(chars)),
+ SR.ArgumentNull_Array);
+
+ if (byteIndex < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - byteIndex < byteCount)
+ throw new ArgumentOutOfRangeException(nameof(bytes),
+ SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (chars.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException(nameof(chars),
+ SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ bytesUsed = byteCount;
+
+ // Its easy to do if it won't overrun our buffer.
+ while (bytesUsed > 0)
+ {
+ if (GetCharCount(bytes, byteIndex, bytesUsed, flush) <= charCount)
+ {
+ charsUsed = GetChars(bytes, byteIndex, bytesUsed, chars, charIndex, flush);
+ completed = (bytesUsed == byteCount &&
+ (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0));
+ return;
+ }
+
+ // Try again with 1/2 the count, won't flush then 'cause won't read it all
+ flush = false;
+ bytesUsed /= 2;
+ }
+
+ // Oops, we didn't have anything, we'll have to throw an overflow
+ throw new ArgumentException(SR.Argument_ConversionOverflow);
+ }
+
+ // This is the version that uses *.
+ // We're done processing this buffer only if completed returns true.
+ //
+ // Might consider checking Max...Count to avoid the extra counting step.
+ //
+ // Note that if all of the input bytes are not consumed, then we'll do a /2, which means
+ // that its likely that we didn't consume as many bytes as we could have. For some
+ // applications this could be slow. (Like trying to exactly fill an output buffer from a bigger stream)
+ [CLSCompliant(false)]
+ public virtual unsafe void Convert(byte* bytes, int byteCount,
+ char* chars, int charCount, bool flush,
+ out int bytesUsed, out int charsUsed, out bool completed)
+ {
+ // Validate input parameters
+ if (chars == null || bytes == null)
+ throw new ArgumentNullException(chars == null ? nameof(chars) : nameof(bytes),
+ SR.ArgumentNull_Array);
+
+ if (byteCount < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((byteCount < 0 ? nameof(byteCount) : nameof(charCount)),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Get ready to do it
+ bytesUsed = byteCount;
+
+ // Its easy to do if it won't overrun our buffer.
+ while (bytesUsed > 0)
+ {
+ if (GetCharCount(bytes, bytesUsed, flush) <= charCount)
+ {
+ charsUsed = GetChars(bytes, bytesUsed, chars, charCount, flush);
+ completed = (bytesUsed == byteCount &&
+ (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0));
+ return;
+ }
+
+ // Try again with 1/2 the count, won't flush then 'cause won't read it all
+ flush = false;
+ bytesUsed /= 2;
+ }
+
+ // Oops, we didn't have anything, we'll have to throw an overflow
+ throw new ArgumentException(SR.Argument_ConversionOverflow);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/Encoder.cs b/src/mscorlib/shared/System/Text/Encoder.cs
new file mode 100644
index 0000000000..e4e91765e1
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/Encoder.cs
@@ -0,0 +1,333 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+using System.Text;
+using System;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+
+namespace System.Text
+{
+ // An Encoder is used to encode a sequence of blocks of characters into
+ // a sequence of blocks of bytes. Following instantiation of an encoder,
+ // sequential blocks of characters are converted into blocks of bytes through
+ // calls to the GetBytes method. The encoder maintains state between the
+ // conversions, allowing it to correctly encode character sequences that span
+ // adjacent blocks.
+ //
+ // Instances of specific implementations of the Encoder abstract base
+ // class are typically obtained through calls to the GetEncoder method
+ // of Encoding objects.
+ //
+ [Serializable]
+ public abstract class Encoder
+ {
+ internal EncoderFallback m_fallback = null;
+
+ [NonSerialized]
+ internal EncoderFallbackBuffer m_fallbackBuffer = null;
+
+ internal void SerializeEncoder(SerializationInfo info)
+ {
+ info.AddValue("m_fallback", this.m_fallback);
+ }
+
+ protected Encoder()
+ {
+ // We don't call default reset because default reset probably isn't good if we aren't initialized.
+ }
+
+ public EncoderFallback Fallback
+ {
+ get
+ {
+ return m_fallback;
+ }
+
+ set
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+ Contract.EndContractBlock();
+
+ // Can't change fallback if buffer is wrong
+ if (m_fallbackBuffer != null && m_fallbackBuffer.Remaining > 0)
+ throw new ArgumentException(
+ SR.Argument_FallbackBufferNotEmpty, nameof(value));
+
+ m_fallback = value;
+ m_fallbackBuffer = null;
+ }
+ }
+
+ // Note: we don't test for threading here because async access to Encoders and Decoders
+ // doesn't work anyway.
+ public EncoderFallbackBuffer FallbackBuffer
+ {
+ get
+ {
+ if (m_fallbackBuffer == null)
+ {
+ if (m_fallback != null)
+ m_fallbackBuffer = m_fallback.CreateFallbackBuffer();
+ else
+ m_fallbackBuffer = EncoderFallback.ReplacementFallback.CreateFallbackBuffer();
+ }
+
+ return m_fallbackBuffer;
+ }
+ }
+
+ internal bool InternalHasFallbackBuffer
+ {
+ get
+ {
+ return m_fallbackBuffer != null;
+ }
+ }
+
+ // Reset the Encoder
+ //
+ // Normally if we call GetBytes() and an error is thrown we don't change the state of the encoder. This
+ // would allow the caller to correct the error condition and try again (such as if they need a bigger buffer.)
+ //
+ // If the caller doesn't want to try again after GetBytes() throws an error, then they need to call Reset().
+ //
+ // Virtual implementation has to call GetBytes with flush and a big enough buffer to clear a 0 char string
+ // We avoid GetMaxByteCount() because a) we can't call the base encoder and b) it might be really big.
+ public virtual void Reset()
+ {
+ char[] charTemp = { };
+ byte[] byteTemp = new byte[GetByteCount(charTemp, 0, 0, true)];
+ GetBytes(charTemp, 0, 0, byteTemp, 0, true);
+ if (m_fallbackBuffer != null)
+ m_fallbackBuffer.Reset();
+ }
+
+ // Returns the number of bytes the next call to GetBytes will
+ // produce if presented with the given range of characters and the given
+ // value of the flush parameter. The returned value takes into
+ // account the state in which the encoder was left following the last call
+ // to GetBytes. The state of the encoder is not affected by a call
+ // to this method.
+ //
+ public abstract int GetByteCount(char[] chars, int index, int count, bool flush);
+
+ // We expect this to be the workhorse for NLS encodings
+ // unfortunately for existing overrides, it has to call the [] version,
+ // which is really slow, so avoid this method if you might be calling external encodings.
+ [CLSCompliant(false)]
+ public virtual unsafe int GetByteCount(char* chars, int count, bool flush)
+ {
+ // Validate input parameters
+ if (chars == null)
+ throw new ArgumentNullException(nameof(chars),
+ SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ char[] arrChar = new char[count];
+ int index;
+
+ for (index = 0; index < count; index++)
+ arrChar[index] = chars[index];
+
+ return GetByteCount(arrChar, 0, count, flush);
+ }
+
+ // Encodes a range of characters in a character array into a range of bytes
+ // in a byte array. The method encodes charCount characters from
+ // chars starting at index charIndex, storing the resulting
+ // bytes in bytes starting at index byteIndex. The encoding
+ // takes into account the state in which the encoder was left following the
+ // last call to this method. The flush parameter indicates whether
+ // the encoder should flush any shift-states and partial characters at the
+ // end of the conversion. To ensure correct termination of a sequence of
+ // blocks of encoded bytes, the last call to GetBytes should specify
+ // a value of true for the flush parameter.
+ //
+ // An exception occurs if the byte array is not large enough to hold the
+ // complete encoding of the characters. The GetByteCount method can
+ // be used to determine the exact number of bytes that will be produced for
+ // a given range of characters. Alternatively, the GetMaxByteCount
+ // method of the Encoding that produced this encoder can be used to
+ // determine the maximum number of bytes that will be produced for a given
+ // number of characters, regardless of the actual character values.
+ //
+ public abstract int GetBytes(char[] chars, int charIndex, int charCount,
+ byte[] bytes, int byteIndex, bool flush);
+
+ // We expect this to be the workhorse for NLS Encodings, but for existing
+ // ones we need a working (if slow) default implementation)
+ //
+ // WARNING WARNING WARNING
+ //
+ // WARNING: If this breaks it could be a security threat. Obviously we
+ // call this internally, so you need to make sure that your pointers, counts
+ // and indexes are correct when you call this method.
+ //
+ // In addition, we have internal code, which will be marked as "safe" calling
+ // this code. However this code is dependent upon the implementation of an
+ // external GetBytes() method, which could be overridden by a third party and
+ // the results of which cannot be guaranteed. We use that result to copy
+ // the byte[] to our byte* output buffer. If the result count was wrong, we
+ // could easily overflow our output buffer. Therefore we do an extra test
+ // when we copy the buffer so that we don't overflow byteCount either.
+ [CLSCompliant(false)]
+ public virtual unsafe int GetBytes(char* chars, int charCount,
+ byte* bytes, int byteCount, bool flush)
+ {
+ // Validate input parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars),
+ SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Get the char array to convert
+ char[] arrChar = new char[charCount];
+
+ int index;
+ for (index = 0; index < charCount; index++)
+ arrChar[index] = chars[index];
+
+ // Get the byte array to fill
+ byte[] arrByte = new byte[byteCount];
+
+ // Do the work
+ int result = GetBytes(arrChar, 0, charCount, arrByte, 0, flush);
+
+ Debug.Assert(result <= byteCount, "Returned more bytes than we have space for");
+
+ // Copy the byte array
+ // WARNING: We MUST make sure that we don't copy too many bytes. We can't
+ // rely on result because it could be a 3rd party implementation. We need
+ // to make sure we never copy more than byteCount bytes no matter the value
+ // of result
+ if (result < byteCount)
+ byteCount = result;
+
+ // Don't copy too many bytes!
+ for (index = 0; index < byteCount; index++)
+ bytes[index] = arrByte[index];
+
+ return byteCount;
+ }
+
+ // This method is used to avoid running out of output buffer space.
+ // It will encode until it runs out of chars, and then it will return
+ // true if it the entire input was converted. In either case it
+ // will also return the number of converted chars and output bytes used.
+ // It will only throw a buffer overflow exception if the entire lenght of bytes[] is
+ // too small to store the next byte. (like 0 or maybe 1 or 4 for some encodings)
+ // We're done processing this buffer only if completed returns true.
+ //
+ // Might consider checking Max...Count to avoid the extra counting step.
+ //
+ // Note that if all of the input chars are not consumed, then we'll do a /2, which means
+ // that its likely that we didn't consume as many chars as we could have. For some
+ // applications this could be slow. (Like trying to exactly fill an output buffer from a bigger stream)
+ public virtual void Convert(char[] chars, int charIndex, int charCount,
+ byte[] bytes, int byteIndex, int byteCount, bool flush,
+ out int charsUsed, out int bytesUsed, out bool completed)
+ {
+ // Validate parameters
+ if (chars == null || bytes == null)
+ throw new ArgumentNullException((chars == null ? nameof(chars) : nameof(bytes)),
+ SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? nameof(charIndex) : nameof(charCount)),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (byteIndex < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((byteIndex < 0 ? nameof(byteIndex) : nameof(byteCount)),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException(nameof(chars),
+ SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (bytes.Length - byteIndex < byteCount)
+ throw new ArgumentOutOfRangeException(nameof(bytes),
+ SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ charsUsed = charCount;
+
+ // Its easy to do if it won't overrun our buffer.
+ // Note: We don't want to call unsafe version because that might be an untrusted version
+ // which could be really unsafe and we don't want to mix it up.
+ while (charsUsed > 0)
+ {
+ if (GetByteCount(chars, charIndex, charsUsed, flush) <= byteCount)
+ {
+ bytesUsed = GetBytes(chars, charIndex, charsUsed, bytes, byteIndex, flush);
+ completed = (charsUsed == charCount &&
+ (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0));
+ return;
+ }
+
+ // Try again with 1/2 the count, won't flush then 'cause won't read it all
+ flush = false;
+ charsUsed /= 2;
+ }
+
+ // Oops, we didn't have anything, we'll have to throw an overflow
+ throw new ArgumentException(SR.Argument_ConversionOverflow);
+ }
+
+ // Same thing, but using pointers
+ //
+ // Might consider checking Max...Count to avoid the extra counting step.
+ //
+ // Note that if all of the input chars are not consumed, then we'll do a /2, which means
+ // that its likely that we didn't consume as many chars as we could have. For some
+ // applications this could be slow. (Like trying to exactly fill an output buffer from a bigger stream)
+ [CLSCompliant(false)]
+ public virtual unsafe void Convert(char* chars, int charCount,
+ byte* bytes, int byteCount, bool flush,
+ out int charsUsed, out int bytesUsed, out bool completed)
+ {
+ // Validate input parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? nameof(bytes) : nameof(chars),
+ SR.ArgumentNull_Array);
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? nameof(charCount) : nameof(byteCount)),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Get ready to do it
+ charsUsed = charCount;
+
+ // Its easy to do if it won't overrun our buffer.
+ while (charsUsed > 0)
+ {
+ if (GetByteCount(chars, charsUsed, flush) <= byteCount)
+ {
+ bytesUsed = GetBytes(chars, charsUsed, bytes, byteCount, flush);
+ completed = (charsUsed == charCount &&
+ (m_fallbackBuffer == null || m_fallbackBuffer.Remaining == 0));
+ return;
+ }
+
+ // Try again with 1/2 the count, won't flush then 'cause won't read it all
+ flush = false;
+ charsUsed /= 2;
+ }
+
+ // Oops, we didn't have anything, we'll have to throw an overflow
+ throw new ArgumentException(SR.Argument_ConversionOverflow);
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Text/EncodingInfo.cs b/src/mscorlib/shared/System/Text/EncodingInfo.cs
new file mode 100644
index 0000000000..360dd7f638
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/EncodingInfo.cs
@@ -0,0 +1,72 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Text;
+
+namespace System.Text
+{
+ [Serializable]
+ public sealed class EncodingInfo
+ {
+ private int iCodePage; // Code Page #
+ private string strEncodingName; // Short name (web name)
+ private string strDisplayName; // Full localized name
+
+ internal EncodingInfo(int codePage, string name, string displayName)
+ {
+ iCodePage = codePage;
+ strEncodingName = name;
+ strDisplayName = displayName;
+ }
+
+
+ public int CodePage
+ {
+ get
+ {
+ return iCodePage;
+ }
+ }
+
+
+ public string Name
+ {
+ get
+ {
+ return strEncodingName;
+ }
+ }
+
+
+ public string DisplayName
+ {
+ get
+ {
+ return strDisplayName;
+ }
+ }
+
+
+ public Encoding GetEncoding()
+ {
+ return Encoding.GetEncoding(iCodePage);
+ }
+
+ public override bool Equals(Object value)
+ {
+ EncodingInfo that = value as EncodingInfo;
+ if (that != null)
+ {
+ return (this.CodePage == that.CodePage);
+ }
+ return (false);
+ }
+
+ public override int GetHashCode()
+ {
+ return this.CodePage;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/EncodingNLS.cs b/src/mscorlib/shared/System/Text/EncodingNLS.cs
new file mode 100644
index 0000000000..205ae26902
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/EncodingNLS.cs
@@ -0,0 +1,322 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics.Contracts;
+using System.Collections;
+using System.Globalization;
+using System.Threading;
+
+namespace System.Text
+{
+ // This class overrides Encoding with the things we need for our NLS Encodings
+ //
+ // All of the GetBytes/Chars GetByte/CharCount methods are just wrappers for the pointer
+ // plus decoder/encoder method that is our real workhorse. Note that this is an internal
+ // class, so our public classes cannot derive from this class. Because of this, all of the
+ // GetBytes/Chars GetByte/CharCount wrapper methods are duplicated in all of our public
+ // encodings, which currently include:
+ //
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, & UnicodeEncoding
+ //
+ // So if you change the wrappers in this class, you must change the wrappers in the other classes
+ // as well because they should have the same behavior.
+
+ [Serializable]
+ internal abstract class EncodingNLS : Encoding
+ {
+ protected EncodingNLS(int codePage) : base(codePage)
+ {
+ }
+
+ // Returns the number of bytes required to encode a range of characters in
+ // a character array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+ public override unsafe int GetByteCount(char[] chars, int index, int count)
+ {
+ // Validate input parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - index < count)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input, return 0, avoid fixed empty array problem
+ if (count == 0)
+ return 0;
+
+ // Just call the pointer version
+ fixed (char* pChars = chars)
+ return GetByteCount(pChars + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+ public override unsafe int GetByteCount(String s)
+ {
+ // Validate input
+ if (s==null)
+ throw new ArgumentNullException("s");
+ Contract.EndContractBlock();
+
+ fixed (char* pChars = s)
+ return GetByteCount(pChars, s.Length, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ public override unsafe int GetByteCount(char* chars, int count)
+ {
+ // Validate Parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Call it with empty encoder
+ return GetByteCount(chars, count, null);
+ }
+
+ // Parent method is safe.
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ public override unsafe int GetBytes(String s, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ if (s == null || bytes == null)
+ throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (s.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like empty arrays
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0])
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // Encodes a range of characters in a character array into a range of bytes
+ // in a byte array. An exception occurs if the byte array is not large
+ // enough to hold the complete encoding of the characters. The
+ // GetByteCount method can be used to determine the exact number of
+ // bytes that will be produced for a given range of characters.
+ // Alternatively, the GetMaxByteCount method can be used to
+ // determine the maximum number of bytes that will be produced for a given
+ // number of characters, regardless of the actual character values.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+ public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ // Validate parameters
+ if (chars == null || bytes == null)
+ throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If nothing to encode return 0, avoid fixed problem
+ if (charCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like empty arrays
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0])
+ // Remember that byteCount is # to decode, not size of array.
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetBytes(chars, charCount, bytes, byteCount, null);
+ }
+
+ // Returns the number of characters produced by decoding a range of bytes
+ // in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+ public override unsafe int GetCharCount(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - index < count)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input just return 0, fixed doesn't like 0 length arrays
+ if (count == 0)
+ return 0;
+
+ // Just call pointer version
+ fixed (byte* pBytes = bytes)
+ return GetCharCount(pBytes + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ public override unsafe int GetCharCount(byte* bytes, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetCharCount(bytes, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+ public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
+ char[] chars, int charIndex)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (byteIndex < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if ( bytes.Length - byteIndex < byteCount)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (charIndex < 0 || charIndex > chars.Length)
+ throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If no input, return 0 & avoid fixed problem
+ if (byteCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int charCount = chars.Length - charIndex;
+
+ // Fixed doesn't like empty arrays
+ if (chars.Length == 0)
+ chars = new char[1];
+
+ fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0])
+ // Remember that charCount is # to decode, not size of array
+ return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetChars(bytes, byteCount, chars, charCount, null);
+ }
+
+ // Returns a string containing the decoded representation of a range of
+ // bytes in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+ public override unsafe String GetString(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - index < count)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // Avoid problems with empty input buffer
+ if (count == 0) return String.Empty;
+
+ fixed (byte* pBytes = bytes)
+ return String.CreateStringFromEncoding(
+ pBytes + index, count, this);
+ }
+
+ public override Decoder GetDecoder()
+ {
+ return new DecoderNLS(this);
+ }
+
+ public override Encoder GetEncoder()
+ {
+ return new EncoderNLS(this);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/EncodingProvider.cs b/src/mscorlib/shared/System/Text/EncodingProvider.cs
new file mode 100644
index 0000000000..ce8c3e0208
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/EncodingProvider.cs
@@ -0,0 +1,136 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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;
+using System.Collections.Generic;
+
+namespace System.Text
+{
+ public abstract class EncodingProvider
+ {
+ public EncodingProvider() { }
+ public abstract Encoding GetEncoding(string name);
+ public abstract Encoding GetEncoding(int codepage);
+
+ // GetEncoding should return either valid encoding or null. shouldn't throw any exception except on null name
+ public virtual Encoding GetEncoding(string name, EncoderFallback encoderFallback, DecoderFallback decoderFallback)
+ {
+ Encoding enc = GetEncoding(name);
+ if (enc != null)
+ {
+ enc = (Encoding)GetEncoding(name).Clone();
+ enc.EncoderFallback = encoderFallback;
+ enc.DecoderFallback = decoderFallback;
+ }
+
+ return enc;
+ }
+
+ public virtual Encoding GetEncoding(int codepage, EncoderFallback encoderFallback, DecoderFallback decoderFallback)
+ {
+ Encoding enc = GetEncoding(codepage);
+ if (enc != null)
+ {
+ enc = (Encoding)GetEncoding(codepage).Clone();
+ enc.EncoderFallback = encoderFallback;
+ enc.DecoderFallback = decoderFallback;
+ }
+
+ return enc;
+ }
+
+ internal static void AddProvider(EncodingProvider provider)
+ {
+ if (provider == null)
+ throw new ArgumentNullException(nameof(provider));
+
+ lock (s_InternalSyncObject)
+ {
+ if (s_providers == null)
+ {
+ s_providers = new EncodingProvider[1] { provider };
+ return;
+ }
+
+ if (Array.IndexOf(s_providers, provider) >= 0)
+ {
+ return;
+ }
+
+ EncodingProvider[] providers = new EncodingProvider[s_providers.Length + 1];
+ Array.Copy(s_providers, providers, s_providers.Length);
+ providers[providers.Length - 1] = provider;
+ s_providers = providers;
+ }
+ }
+
+ internal static Encoding GetEncodingFromProvider(int codepage)
+ {
+ if (s_providers == null)
+ return null;
+
+ EncodingProvider[] providers = s_providers;
+ foreach (EncodingProvider provider in providers)
+ {
+ Encoding enc = provider.GetEncoding(codepage);
+ if (enc != null)
+ return enc;
+ }
+
+ return null;
+ }
+
+ internal static Encoding GetEncodingFromProvider(string encodingName)
+ {
+ if (s_providers == null)
+ return null;
+
+ EncodingProvider[] providers = s_providers;
+ foreach (EncodingProvider provider in providers)
+ {
+ Encoding enc = provider.GetEncoding(encodingName);
+ if (enc != null)
+ return enc;
+ }
+
+ return null;
+ }
+
+ internal static Encoding GetEncodingFromProvider(int codepage, EncoderFallback enc, DecoderFallback dec)
+ {
+ if (s_providers == null)
+ return null;
+
+ EncodingProvider[] providers = s_providers;
+ foreach (EncodingProvider provider in providers)
+ {
+ Encoding encing = provider.GetEncoding(codepage, enc, dec);
+ if (encing != null)
+ return encing;
+ }
+
+ return null;
+ }
+
+ internal static Encoding GetEncodingFromProvider(string encodingName, EncoderFallback enc, DecoderFallback dec)
+ {
+ if (s_providers == null)
+ return null;
+
+ EncodingProvider[] providers = s_providers;
+ foreach (EncodingProvider provider in providers)
+ {
+ Encoding encoding = provider.GetEncoding(encodingName, enc, dec);
+ if (encoding != null)
+ return encoding;
+ }
+
+ return null;
+ }
+
+ private static Object s_InternalSyncObject = new Object();
+ private static volatile EncodingProvider[] s_providers;
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/Normalization.cs b/src/mscorlib/shared/System/Text/Normalization.cs
new file mode 100644
index 0000000000..dc8bc2af71
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/Normalization.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text
+{
+ // This is the enumeration for Normalization Forms
+ public enum NormalizationForm
+ {
+ FormC = 1,
+ FormD = 2,
+ FormKC = 5,
+ FormKD = 6
+ }
+
+ internal enum ExtendedNormalizationForms
+ {
+ FormC = 1,
+ FormD = 2,
+ FormKC = 5,
+ FormKD = 6,
+ FormIdna = 0xd,
+ FormCDisallowUnassigned = 0x101,
+ FormDDisallowUnassigned = 0x102,
+ FormKCDisallowUnassigned = 0x105,
+ FormKDDisallowUnassigned = 0x106,
+ FormIdnaDisallowUnassigned = 0x10d
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/StringBuilder.cs b/src/mscorlib/shared/System/Text/StringBuilder.cs
new file mode 100644
index 0000000000..df1a889823
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/StringBuilder.cs
@@ -0,0 +1,2409 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime;
+using System.Runtime.Serialization;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+using System.Security;
+using System.Threading;
+using System.Globalization;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Collections.Generic;
+
+namespace System.Text
+{
+ // This class represents a mutable string. It is convenient for situations in
+ // which it is desirable to modify a string, perhaps by removing, replacing, or
+ // inserting characters, without creating a new String subsequent to
+ // each modification.
+ //
+ // The methods contained within this class do not return a new StringBuilder
+ // object unless specified otherwise. This class may be used in conjunction with the String
+ // class to carry out modifications upon strings.
+ //
+ // When passing null into a constructor in VJ and VC, the null
+ // should be explicitly type cast.
+ // For Example:
+ // StringBuilder sb1 = new StringBuilder((StringBuilder)null);
+ // StringBuilder sb2 = new StringBuilder((String)null);
+ // Console.WriteLine(sb1);
+ // Console.WriteLine(sb2);
+ //
+ [Serializable]
+ public sealed partial class StringBuilder : ISerializable
+ {
+ // A StringBuilder is internally represented as a linked list of blocks each of which holds
+ // a chunk of the string. It turns out string as a whole can also be represented as just a chunk,
+ // so that is what we do.
+
+ //
+ //
+ // CLASS VARIABLES
+ //
+ //
+ internal char[] m_ChunkChars; // The characters in this block
+ internal StringBuilder m_ChunkPrevious; // Link to the block logically before this block
+ internal int m_ChunkLength; // The index in m_ChunkChars that represent the end of the block
+ internal int m_ChunkOffset; // The logical offset (sum of all characters in previous blocks)
+ internal int m_MaxCapacity = 0;
+
+ //
+ //
+ // STATIC CONSTANTS
+ //
+ //
+ internal const int DefaultCapacity = 16;
+ private const String CapacityField = "Capacity";
+ private const String MaxCapacityField = "m_MaxCapacity";
+ private const String StringValueField = "m_StringValue";
+ private const String ThreadIDField = "m_currentThread";
+ // We want to keep chunk arrays out of large object heap (< 85K bytes ~ 40K chars) to be sure.
+ // Making the maximum chunk size big means less allocation code called, but also more waste
+ // in unused characters and slower inserts / replaces (since you do need to slide characters over
+ // within a buffer).
+ internal const int MaxChunkSize = 8000;
+
+ //
+ //
+ //CONSTRUCTORS
+ //
+ //
+
+ // Creates a new empty string builder (i.e., it represents String.Empty)
+ // with the default capacity (16 characters).
+ public StringBuilder()
+ {
+ m_MaxCapacity = int.MaxValue;
+ m_ChunkChars = new char[DefaultCapacity];
+ }
+
+ // Create a new empty string builder (i.e., it represents String.Empty)
+ // with the specified capacity.
+ public StringBuilder(int capacity)
+ : this(capacity, int.MaxValue)
+ {
+ }
+
+ // Creates a new string builder from the specified string. If value
+ // is a null String (i.e., if it represents String.NullString)
+ // then the new string builder will also be null (i.e., it will also represent
+ // String.NullString).
+ //
+ public StringBuilder(String value)
+ : this(value, DefaultCapacity)
+ {
+ }
+
+ // Creates a new string builder from the specified string with the specified
+ // capacity. If value is a null String (i.e., if it represents
+ // String.NullString) then the new string builder will also be null
+ // (i.e., it will also represent String.NullString).
+ // The maximum number of characters this string may contain is set by capacity.
+ //
+ public StringBuilder(String value, int capacity)
+ : this(value, 0, ((value != null) ? value.Length : 0), capacity)
+ {
+ }
+
+ // Creates a new string builder from the specifed substring with the specified
+ // capacity. The maximum number of characters is set by capacity.
+ //
+ public StringBuilder(String value, int startIndex, int length, int capacity)
+ {
+ if (capacity < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity),
+ SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(capacity)));
+ }
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length),
+ SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(length)));
+ }
+ if (startIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+ }
+ Contract.EndContractBlock();
+
+ if (value == null)
+ {
+ value = String.Empty;
+ }
+ if (startIndex > value.Length - length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_IndexLength);
+ }
+ m_MaxCapacity = Int32.MaxValue;
+ if (capacity == 0)
+ {
+ capacity = DefaultCapacity;
+ }
+ if (capacity < length)
+ capacity = length;
+
+ m_ChunkChars = new char[capacity];
+ m_ChunkLength = length;
+
+ unsafe
+ {
+ fixed (char* sourcePtr = value)
+ ThreadSafeCopy(sourcePtr + startIndex, m_ChunkChars, 0, length);
+ }
+ }
+
+ // Creates an empty StringBuilder with a minimum capacity of capacity
+ // and a maximum capacity of maxCapacity.
+ public StringBuilder(int capacity, int maxCapacity)
+ {
+ if (capacity > maxCapacity)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_Capacity);
+ }
+ if (maxCapacity < 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(maxCapacity), SR.ArgumentOutOfRange_SmallMaxCapacity);
+ }
+ if (capacity < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity),
+ SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(capacity)));
+ }
+ Contract.EndContractBlock();
+
+ if (capacity == 0)
+ {
+ capacity = Math.Min(DefaultCapacity, maxCapacity);
+ }
+
+ m_MaxCapacity = maxCapacity;
+ m_ChunkChars = new char[capacity];
+ }
+
+ private StringBuilder(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ int persistedCapacity = 0;
+ string persistedString = null;
+ int persistedMaxCapacity = Int32.MaxValue;
+ bool capacityPresent = false;
+
+ // Get the data
+ SerializationInfoEnumerator enumerator = info.GetEnumerator();
+ while (enumerator.MoveNext())
+ {
+ switch (enumerator.Name)
+ {
+ case MaxCapacityField:
+ persistedMaxCapacity = info.GetInt32(MaxCapacityField);
+ break;
+ case StringValueField:
+ persistedString = info.GetString(StringValueField);
+ break;
+ case CapacityField:
+ persistedCapacity = info.GetInt32(CapacityField);
+ capacityPresent = true;
+ break;
+ default:
+ // Ignore other fields for forward compatibility.
+ break;
+ }
+ }
+
+ // Check values and set defaults
+ if (persistedString == null)
+ {
+ persistedString = String.Empty;
+ }
+ if (persistedMaxCapacity < 1 || persistedString.Length > persistedMaxCapacity)
+ {
+ throw new SerializationException(SR.Serialization_StringBuilderMaxCapacity);
+ }
+
+ if (!capacityPresent)
+ {
+ // StringBuilder in V1.X did not persist the Capacity, so this is a valid legacy code path.
+ persistedCapacity = DefaultCapacity;
+ if (persistedCapacity < persistedString.Length)
+ {
+ persistedCapacity = persistedString.Length;
+ }
+ if (persistedCapacity > persistedMaxCapacity)
+ {
+ persistedCapacity = persistedMaxCapacity;
+ }
+ }
+ if (persistedCapacity < 0 || persistedCapacity < persistedString.Length || persistedCapacity > persistedMaxCapacity)
+ {
+ throw new SerializationException(SR.Serialization_StringBuilderCapacity);
+ }
+
+ // Assign
+ m_MaxCapacity = persistedMaxCapacity;
+ m_ChunkChars = new char[persistedCapacity];
+ persistedString.CopyTo(0, m_ChunkChars, 0, persistedString.Length);
+ m_ChunkLength = persistedString.Length;
+ m_ChunkPrevious = null;
+ VerifyClassInvariant();
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+ Contract.EndContractBlock();
+
+ VerifyClassInvariant();
+ info.AddValue(MaxCapacityField, m_MaxCapacity);
+ info.AddValue(CapacityField, Capacity);
+ info.AddValue(StringValueField, ToString());
+ // Note: persist "m_currentThread" to be compatible with old versions
+ info.AddValue(ThreadIDField, 0);
+ }
+
+ [System.Diagnostics.Conditional("_DEBUG")]
+ private void VerifyClassInvariant()
+ {
+ Debug.Assert((uint)(m_ChunkOffset + m_ChunkChars.Length) >= m_ChunkOffset, "Integer Overflow");
+ StringBuilder currentBlock = this;
+ int maxCapacity = this.m_MaxCapacity;
+ for (;;)
+ {
+ // All blocks have copy of the maxCapacity.
+ Debug.Assert(currentBlock.m_MaxCapacity == maxCapacity, "Bad maxCapacity");
+ Debug.Assert(currentBlock.m_ChunkChars != null, "Empty Buffer");
+
+ Debug.Assert(currentBlock.m_ChunkLength <= currentBlock.m_ChunkChars.Length, "Out of range length");
+ Debug.Assert(currentBlock.m_ChunkLength >= 0, "Negative length");
+ Debug.Assert(currentBlock.m_ChunkOffset >= 0, "Negative offset");
+
+ StringBuilder prevBlock = currentBlock.m_ChunkPrevious;
+ if (prevBlock == null)
+ {
+ Debug.Assert(currentBlock.m_ChunkOffset == 0, "First chunk's offset is not 0");
+ break;
+ }
+ // There are no gaps in the blocks.
+ Debug.Assert(currentBlock.m_ChunkOffset == prevBlock.m_ChunkOffset + prevBlock.m_ChunkLength, "There is a gap between chunks!");
+ currentBlock = prevBlock;
+ }
+ }
+
+ public int Capacity
+ {
+ get { return m_ChunkChars.Length + m_ChunkOffset; }
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NegativeCapacity);
+ }
+ if (value > MaxCapacity)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_Capacity);
+ }
+ if (value < Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity);
+ }
+ Contract.EndContractBlock();
+
+ if (Capacity != value)
+ {
+ int newLen = value - m_ChunkOffset;
+ char[] newArray = new char[newLen];
+ Array.Copy(m_ChunkChars, 0, newArray, 0, m_ChunkLength);
+ m_ChunkChars = newArray;
+ }
+ }
+ }
+
+ public int MaxCapacity
+ {
+ get { return m_MaxCapacity; }
+ }
+
+ // Ensures that the capacity of this string builder is at least the specified value.
+ // If capacity is greater than the capacity of this string builder, then the capacity
+ // is set to capacity; otherwise the capacity is unchanged.
+ //
+ public int EnsureCapacity(int capacity)
+ {
+ if (capacity < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NegativeCapacity);
+ }
+ Contract.EndContractBlock();
+
+ if (Capacity < capacity)
+ Capacity = capacity;
+ return Capacity;
+ }
+
+ public override String ToString()
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+
+ VerifyClassInvariant();
+
+ if (Length == 0)
+ return String.Empty;
+
+ string ret = string.FastAllocateString(Length);
+ StringBuilder chunk = this;
+ unsafe
+ {
+ fixed (char* destinationPtr = ret)
+ {
+ do
+ {
+ if (chunk.m_ChunkLength > 0)
+ {
+ // Copy these into local variables so that they are stable even in the presence of race conditions
+ char[] sourceArray = chunk.m_ChunkChars;
+ int chunkOffset = chunk.m_ChunkOffset;
+ int chunkLength = chunk.m_ChunkLength;
+
+ // Check that we will not overrun our boundaries.
+ if ((uint)(chunkLength + chunkOffset) <= (uint)ret.Length && (uint)chunkLength <= (uint)sourceArray.Length)
+ {
+ fixed (char* sourcePtr = &sourceArray[0])
+ string.wstrcpy(destinationPtr + chunkOffset, sourcePtr, chunkLength);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(chunkLength), SR.ArgumentOutOfRange_Index);
+ }
+ }
+ chunk = chunk.m_ChunkPrevious;
+ } while (chunk != null);
+
+ return ret;
+ }
+ }
+ }
+
+
+ // Converts a substring of this string builder to a String.
+ public String ToString(int startIndex, int length)
+ {
+ Contract.Ensures(Contract.Result<String>() != null);
+
+ int currentLength = this.Length;
+ if (startIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+ }
+ if (startIndex > currentLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndexLargerThanLength);
+ }
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
+ }
+ if (startIndex > (currentLength - length))
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_IndexLength);
+ }
+
+ VerifyClassInvariant();
+
+ StringBuilder chunk = this;
+ int sourceEndIndex = startIndex + length;
+
+ string ret = string.FastAllocateString(length);
+ int curDestIndex = length;
+ unsafe
+ {
+ fixed (char* destinationPtr = ret)
+ {
+ while (curDestIndex > 0)
+ {
+ int chunkEndIndex = sourceEndIndex - chunk.m_ChunkOffset;
+ if (chunkEndIndex >= 0)
+ {
+ if (chunkEndIndex > chunk.m_ChunkLength)
+ chunkEndIndex = chunk.m_ChunkLength;
+
+ int countLeft = curDestIndex;
+ int chunkCount = countLeft;
+ int chunkStartIndex = chunkEndIndex - countLeft;
+ if (chunkStartIndex < 0)
+ {
+ chunkCount += chunkStartIndex;
+ chunkStartIndex = 0;
+ }
+ curDestIndex -= chunkCount;
+
+ if (chunkCount > 0)
+ {
+ // work off of local variables so that they are stable even in the presence of race conditions
+ char[] sourceArray = chunk.m_ChunkChars;
+
+ // Check that we will not overrun our boundaries.
+ if ((uint)(chunkCount + curDestIndex) <= (uint)length && (uint)(chunkCount + chunkStartIndex) <= (uint)sourceArray.Length)
+ {
+ fixed (char* sourcePtr = &sourceArray[chunkStartIndex])
+ string.wstrcpy(destinationPtr + curDestIndex, sourcePtr, chunkCount);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(chunkCount), SR.ArgumentOutOfRange_Index);
+ }
+ }
+ }
+ chunk = chunk.m_ChunkPrevious;
+ }
+
+ return ret;
+ }
+ }
+ }
+
+ // Convenience method for sb.Length=0;
+ public StringBuilder Clear()
+ {
+ this.Length = 0;
+ return this;
+ }
+
+ // Sets the length of the String in this buffer. If length is less than the current
+ // instance, the StringBuilder is truncated. If length is greater than the current
+ // instance, nulls are appended. The capacity is adjusted to be the same as the length.
+
+ public int Length
+ {
+ get
+ {
+ Contract.Ensures(Contract.Result<int>() >= 0);
+ return m_ChunkOffset + m_ChunkLength;
+ }
+ set
+ {
+ //If the new length is less than 0 or greater than our Maximum capacity, bail.
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_NegativeLength);
+ }
+
+ if (value > MaxCapacity)
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity);
+ }
+ Contract.EndContractBlock();
+
+ int originalCapacity = Capacity;
+
+ if (value == 0 && m_ChunkPrevious == null)
+ {
+ m_ChunkLength = 0;
+ m_ChunkOffset = 0;
+ Debug.Assert(Capacity >= originalCapacity, "setting the Length should never decrease the Capacity");
+ return;
+ }
+
+ int delta = value - Length;
+ // if the specified length is greater than the current length
+ if (delta > 0)
+ {
+ // the end of the string value of the current StringBuilder object is padded with the Unicode NULL character
+ Append('\0', delta); // We could improve on this, but who does this anyway?
+ }
+ // if the specified length is less than or equal to the current length
+ else
+ {
+ StringBuilder chunk = FindChunkForIndex(value);
+ if (chunk != this)
+ {
+ // we crossed a chunk boundary when reducing the Length, we must replace this middle-chunk with a new
+ // larger chunk to ensure the original capacity is preserved
+ int newLen = originalCapacity - chunk.m_ChunkOffset;
+ char[] newArray = new char[newLen];
+
+ Debug.Assert(newLen > chunk.m_ChunkChars.Length, "the new chunk should be larger than the one it is replacing");
+ Array.Copy(chunk.m_ChunkChars, 0, newArray, 0, chunk.m_ChunkLength);
+
+ m_ChunkChars = newArray;
+ m_ChunkPrevious = chunk.m_ChunkPrevious;
+ m_ChunkOffset = chunk.m_ChunkOffset;
+ }
+ m_ChunkLength = value - chunk.m_ChunkOffset;
+ VerifyClassInvariant();
+ }
+ Debug.Assert(Capacity >= originalCapacity, "setting the Length should never decrease the Capacity");
+ }
+ }
+
+ [System.Runtime.CompilerServices.IndexerName("Chars")]
+ public char this[int index]
+ {
+ get
+ {
+ StringBuilder chunk = this;
+ for (;;)
+ {
+ int indexInBlock = index - chunk.m_ChunkOffset;
+ if (indexInBlock >= 0)
+ {
+ if (indexInBlock >= chunk.m_ChunkLength)
+ throw new IndexOutOfRangeException();
+ return chunk.m_ChunkChars[indexInBlock];
+ }
+ chunk = chunk.m_ChunkPrevious;
+ if (chunk == null)
+ throw new IndexOutOfRangeException();
+ }
+ }
+ set
+ {
+ StringBuilder chunk = this;
+ for (;;)
+ {
+ int indexInBlock = index - chunk.m_ChunkOffset;
+ if (indexInBlock >= 0)
+ {
+ if (indexInBlock >= chunk.m_ChunkLength)
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
+ chunk.m_ChunkChars[indexInBlock] = value;
+ return;
+ }
+ chunk = chunk.m_ChunkPrevious;
+ if (chunk == null)
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
+ }
+ }
+ }
+
+ // Appends a character at the end of this string builder. The capacity is adjusted as needed.
+ public StringBuilder Append(char value, int repeatCount)
+ {
+ if (repeatCount < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(repeatCount), SR.ArgumentOutOfRange_NegativeCount);
+ }
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ Contract.EndContractBlock();
+
+ if (repeatCount == 0)
+ {
+ return this;
+ }
+
+ // this is where we can check if the repeatCount will put us over m_MaxCapacity
+ // We are doing the check here to prevent the corruption of the StringBuilder.
+ int newLength = Length + repeatCount;
+ if (newLength > m_MaxCapacity || newLength < repeatCount)
+ {
+ throw new ArgumentOutOfRangeException(nameof(repeatCount), SR.ArgumentOutOfRange_LengthGreaterThanCapacity);
+ }
+
+ int idx = m_ChunkLength;
+ while (repeatCount > 0)
+ {
+ if (idx < m_ChunkChars.Length)
+ {
+ m_ChunkChars[idx++] = value;
+ --repeatCount;
+ }
+ else
+ {
+ m_ChunkLength = idx;
+ ExpandByABlock(repeatCount);
+ Debug.Assert(m_ChunkLength == 0, "Expand should create a new block");
+ idx = 0;
+ }
+ }
+ m_ChunkLength = idx;
+ VerifyClassInvariant();
+ return this;
+ }
+
+ // Appends an array of characters at the end of this string builder. The capacity is adjusted as needed.
+ public StringBuilder Append(char[] value, int startIndex, int charCount)
+ {
+ if (startIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_GenericPositive);
+ }
+ if (charCount < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GenericPositive);
+ }
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ Contract.EndContractBlock();
+
+ if (value == null)
+ {
+ if (startIndex == 0 && charCount == 0)
+ {
+ return this;
+ }
+ throw new ArgumentNullException(nameof(value));
+ }
+ if (charCount > value.Length - startIndex)
+ {
+ throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_Index);
+ }
+
+ if (charCount == 0)
+ {
+ return this;
+ }
+ unsafe
+ {
+ fixed (char* valueChars = &value[startIndex])
+ {
+ Append(valueChars, charCount);
+
+ return this;
+ }
+ }
+ }
+
+
+ // Appends a copy of this string at the end of this string builder.
+ public StringBuilder Append(String value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ if (value != null)
+ {
+ // This is a hand specialization of the 'AppendHelper' code below.
+ // We could have just called AppendHelper.
+ char[] chunkChars = m_ChunkChars;
+ int chunkLength = m_ChunkLength;
+ int valueLen = value.Length;
+ int newCurrentIndex = chunkLength + valueLen;
+ if (newCurrentIndex < chunkChars.Length) // Use strictly < to avoid issue if count == 0, newIndex == length
+ {
+ if (valueLen <= 2)
+ {
+ if (valueLen > 0)
+ chunkChars[chunkLength] = value[0];
+ if (valueLen > 1)
+ chunkChars[chunkLength + 1] = value[1];
+ }
+ else
+ {
+ unsafe
+ {
+ fixed (char* valuePtr = value)
+ fixed (char* destPtr = &chunkChars[chunkLength])
+ string.wstrcpy(destPtr, valuePtr, valueLen);
+ }
+ }
+ m_ChunkLength = newCurrentIndex;
+ }
+ else
+ AppendHelper(value);
+ }
+ return this;
+ }
+
+
+ // We put this fixed in its own helper to avoid the cost zero initing valueChars in the
+ // case we don't actually use it.
+ private void AppendHelper(string value)
+ {
+ unsafe
+ {
+ fixed (char* valueChars = value)
+ Append(valueChars, value.Length);
+ }
+ }
+
+ // Appends a copy of the characters in value from startIndex to startIndex +
+ // count at the end of this string builder.
+ public StringBuilder Append(String value, int startIndex, int count)
+ {
+ if (startIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+ }
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GenericPositive);
+ }
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ //If the value being added is null, eat the null
+ //and return.
+ if (value == null)
+ {
+ if (startIndex == 0 && count == 0)
+ {
+ return this;
+ }
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ if (count == 0)
+ {
+ return this;
+ }
+
+ if (startIndex > value.Length - count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+ }
+
+ unsafe
+ {
+ fixed (char* valueChars = value)
+ {
+ Append(valueChars + startIndex, count);
+
+ return this;
+ }
+ }
+ }
+
+ public StringBuilder AppendLine()
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(Environment.NewLine);
+ }
+
+ public StringBuilder AppendLine(string value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ Append(value);
+ return Append(Environment.NewLine);
+ }
+
+ public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
+ {
+ if (destination == null)
+ {
+ throw new ArgumentNullException(nameof(destination));
+ }
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.Arg_NegativeArgCount);
+ }
+
+ if (destinationIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(destinationIndex),
+ SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(destinationIndex)));
+ }
+
+ if (destinationIndex > destination.Length - count)
+ {
+ throw new ArgumentException(SR.ArgumentOutOfRange_OffsetOut);
+ }
+
+ if ((uint)sourceIndex > (uint)Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index);
+ }
+
+ if (sourceIndex > Length - count)
+ {
+ throw new ArgumentException(SR.Arg_LongerThanSrcString);
+ }
+ Contract.EndContractBlock();
+
+ VerifyClassInvariant();
+
+ StringBuilder chunk = this;
+ int sourceEndIndex = sourceIndex + count;
+ int curDestIndex = destinationIndex + count;
+ while (count > 0)
+ {
+ int chunkEndIndex = sourceEndIndex - chunk.m_ChunkOffset;
+ if (chunkEndIndex >= 0)
+ {
+ if (chunkEndIndex > chunk.m_ChunkLength)
+ chunkEndIndex = chunk.m_ChunkLength;
+
+ int chunkCount = count;
+ int chunkStartIndex = chunkEndIndex - count;
+ if (chunkStartIndex < 0)
+ {
+ chunkCount += chunkStartIndex;
+ chunkStartIndex = 0;
+ }
+ curDestIndex -= chunkCount;
+ count -= chunkCount;
+
+ // SafeCritical: we ensure that chunkStartIndex + chunkCount are within range of m_chunkChars
+ // as well as ensuring that curDestIndex + chunkCount are within range of destination
+ ThreadSafeCopy(chunk.m_ChunkChars, chunkStartIndex, destination, curDestIndex, chunkCount);
+ }
+ chunk = chunk.m_ChunkPrevious;
+ }
+ }
+
+ // Inserts multiple copies of a string into this string builder at the specified position.
+ // Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, this
+ // string builder is not changed.
+ //
+ public StringBuilder Insert(int index, String value, int count)
+ {
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
+ }
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ Contract.EndContractBlock();
+
+ //Range check the index.
+ int currentLength = Length;
+ if ((uint)index > (uint)currentLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
+ }
+
+ //If value is null, empty or count is 0, do nothing. This is ECMA standard.
+ if (value == null || value.Length == 0 || count == 0)
+ {
+ return this;
+ }
+
+ //Ensure we don't insert more chars than we can hold, and we don't
+ //have any integer overflow in our inserted characters.
+ long insertingChars = (long)value.Length * count;
+ if (insertingChars > MaxCapacity - this.Length)
+ {
+ throw new OutOfMemoryException();
+ }
+ Debug.Assert(insertingChars + this.Length < Int32.MaxValue);
+
+ StringBuilder chunk;
+ int indexInChunk;
+ MakeRoom(index, (int)insertingChars, out chunk, out indexInChunk, false);
+ unsafe
+ {
+ fixed (char* valuePtr = value)
+ {
+ while (count > 0)
+ {
+ ReplaceInPlaceAtChunk(ref chunk, ref indexInChunk, valuePtr, value.Length);
+ --count;
+ }
+
+ return this;
+ }
+ }
+ }
+
+ // Removes the specified characters from this string builder.
+ // The length of this string builder is reduced by
+ // length, but the capacity is unaffected.
+ //
+ public StringBuilder Remove(int startIndex, int length)
+ {
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
+ }
+
+ if (startIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+ }
+
+ if (length > Length - startIndex)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index);
+ }
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ Contract.EndContractBlock();
+
+ if (Length == length && startIndex == 0)
+ {
+ // Optimization. If we are deleting everything
+ Length = 0;
+ return this;
+ }
+
+ if (length > 0)
+ {
+ StringBuilder chunk;
+ int indexInChunk;
+ Remove(startIndex, length, out chunk, out indexInChunk);
+ }
+ return this;
+ }
+
+ // Appends a boolean to the end of this string builder.
+ // The capacity is adjusted as needed.
+ public StringBuilder Append(bool value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends an sbyte to this string builder.
+ // The capacity is adjusted as needed.
+ [CLSCompliant(false)]
+ public StringBuilder Append(sbyte value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends a ubyte to this string builder.
+ // The capacity is adjusted as needed.
+ public StringBuilder Append(byte value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends a character at the end of this string builder. The capacity is adjusted as needed.
+ public StringBuilder Append(char value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ if (m_ChunkLength < m_ChunkChars.Length)
+ m_ChunkChars[m_ChunkLength++] = value;
+ else
+ Append(value, 1);
+ return this;
+ }
+
+ // Appends a short to this string builder.
+ // The capacity is adjusted as needed.
+ public StringBuilder Append(short value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends an int to this string builder.
+ // The capacity is adjusted as needed.
+ public StringBuilder Append(int value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends a long to this string builder.
+ // The capacity is adjusted as needed.
+ public StringBuilder Append(long value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends a float to this string builder.
+ // The capacity is adjusted as needed.
+ public StringBuilder Append(float value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends a double to this string builder.
+ // The capacity is adjusted as needed.
+ public StringBuilder Append(double value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ public StringBuilder Append(decimal value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends an ushort to this string builder.
+ // The capacity is adjusted as needed.
+ [CLSCompliant(false)]
+ public StringBuilder Append(ushort value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends an uint to this string builder.
+ // The capacity is adjusted as needed.
+ [CLSCompliant(false)]
+ public StringBuilder Append(uint value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends an unsigned long to this string builder.
+ // The capacity is adjusted as needed.
+ [CLSCompliant(false)]
+ public StringBuilder Append(ulong value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Append(value.ToString());
+ }
+
+ // Appends an Object to this string builder.
+ // The capacity is adjusted as needed.
+ public StringBuilder Append(Object value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ if (null == value)
+ {
+ //Appending null is now a no-op.
+ return this;
+ }
+ return Append(value.ToString());
+ }
+
+ // Appends all of the characters in value to the current instance.
+ public StringBuilder Append(char[] value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ if (null != value && value.Length > 0)
+ {
+ unsafe
+ {
+ fixed (char* valueChars = &value[0])
+ Append(valueChars, value.Length);
+ }
+ }
+ return this;
+ }
+
+ // Append joined values with a separator between each value.
+ public unsafe StringBuilder AppendJoin<T>(char separator, params T[] values)
+ {
+ // Defer argument validation to the internal function
+ return AppendJoinCore(&separator, 1, values);
+ }
+
+ public unsafe StringBuilder AppendJoin<T>(string separator, params T[] values)
+ {
+ separator = separator ?? string.Empty;
+ fixed (char* pSeparator = separator)
+ {
+ // Defer argument validation to the internal function
+ return AppendJoinCore(pSeparator, separator.Length, values);
+ }
+ }
+
+ public unsafe StringBuilder AppendJoin<T>(char separator, IEnumerable<T> values)
+ {
+ // Defer argument validation to the internal function
+ return AppendJoinCore(&separator, 1, values);
+ }
+
+ public unsafe StringBuilder AppendJoin<T>(string separator, IEnumerable<T> values)
+ {
+ separator = separator ?? string.Empty;
+ fixed (char* pSeparator = separator)
+ {
+ // Defer argument validation to the internal function
+ return AppendJoinCore(pSeparator, separator.Length, values);
+ }
+ }
+
+ private unsafe StringBuilder AppendJoinCore<T>(char* separator, int separatorLength, params T[] values)
+ {
+ if (values == null)
+ throw new ArgumentNullException(nameof(values));
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ if (values.Length == 0)
+ return this;
+
+ var value = values[0];
+ if (value != null)
+ Append(value.ToString());
+
+ for (var i = 1; i < values.Length; i++)
+ {
+ Append(separator, separatorLength);
+ value = values[i];
+ if (value != null)
+ Append(value.ToString());
+ }
+ return this;
+ }
+
+ private unsafe StringBuilder AppendJoinCore<T>(char* separator, int separatorLength, IEnumerable<T> values)
+ {
+ if (values == null)
+ throw new ArgumentNullException(nameof(values));
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ using (var en = values.GetEnumerator())
+ {
+ if (!en.MoveNext())
+ return this;
+
+ var value = en.Current;
+ if (value != null)
+ Append(value.ToString());
+
+ while (en.MoveNext())
+ {
+ Append(separator, separatorLength);
+ value = en.Current;
+ if (value != null)
+ Append(value.ToString());
+ }
+ }
+ return this;
+ }
+
+ /*====================================Insert====================================
+ **
+ ==============================================================================*/
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ public StringBuilder Insert(int index, String value)
+ {
+ if ((uint)index > (uint)Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
+ }
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ Contract.EndContractBlock();
+
+ if (value != null)
+ {
+ unsafe
+ {
+ fixed (char* sourcePtr = value)
+ Insert(index, sourcePtr, value.Length);
+ }
+ }
+ return this;
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ public StringBuilder Insert(int index, bool value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ [CLSCompliant(false)]
+ public StringBuilder Insert(int index, sbyte value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ public StringBuilder Insert(int index, byte value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ public StringBuilder Insert(int index, short value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ public StringBuilder Insert(int index, char value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ unsafe
+ {
+ Insert(index, &value, 1);
+ }
+ return this;
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ public StringBuilder Insert(int index, char[] value)
+ {
+ if ((uint)index > (uint)Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
+ }
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ Contract.EndContractBlock();
+
+ if (value != null)
+ Insert(index, value, 0, value.Length);
+ return this;
+ }
+
+ // Returns a reference to the StringBuilder with charCount characters from
+ // value inserted into the buffer at index. Existing characters are shifted
+ // to make room for the new text and capacity is adjusted as required. If value is null, the StringBuilder
+ // is unchanged. Characters are taken from value starting at position startIndex.
+ public StringBuilder Insert(int index, char[] value, int startIndex, int charCount)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ int currentLength = Length;
+ if ((uint)index > (uint)currentLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
+ }
+
+ //If they passed in a null char array, just jump out quickly.
+ if (value == null)
+ {
+ if (startIndex == 0 && charCount == 0)
+ {
+ return this;
+ }
+ throw new ArgumentNullException(nameof(value), SR.ArgumentNull_String);
+ }
+
+ //Range check the array.
+ if (startIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
+ }
+
+ if (charCount < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GenericPositive);
+ }
+
+ if (startIndex > value.Length - charCount)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+ }
+
+ if (charCount > 0)
+ {
+ unsafe
+ {
+ fixed (char* sourcePtr = &value[startIndex])
+ Insert(index, sourcePtr, charCount);
+ }
+ }
+ return this;
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ public StringBuilder Insert(int index, int value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ public StringBuilder Insert(int index, long value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ public StringBuilder Insert(int index, float value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with ; value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed.
+ //
+ public StringBuilder Insert(int index, double value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ public StringBuilder Insert(int index, decimal value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed.
+ //
+ [CLSCompliant(false)]
+ public StringBuilder Insert(int index, ushort value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed.
+ //
+ [CLSCompliant(false)]
+ public StringBuilder Insert(int index, uint value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to the StringBuilder with value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the new text.
+ // The capacity is adjusted as needed.
+ //
+ [CLSCompliant(false)]
+ public StringBuilder Insert(int index, ulong value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Insert(index, value.ToString(), 1);
+ }
+
+ // Returns a reference to this string builder with value inserted into
+ // the buffer at index. Existing characters are shifted to make room for the
+ // new text. The capacity is adjusted as needed. If value equals String.Empty, the
+ // StringBuilder is not changed. No changes are made if value is null.
+ //
+ public StringBuilder Insert(int index, Object value)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ if (null == value)
+ {
+ return this;
+ }
+ return Insert(index, value.ToString(), 1);
+ }
+
+ public StringBuilder AppendFormat(String format, Object arg0)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return AppendFormatHelper(null, format, new ParamsArray(arg0));
+ }
+
+ public StringBuilder AppendFormat(String format, Object arg0, Object arg1)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return AppendFormatHelper(null, format, new ParamsArray(arg0, arg1));
+ }
+
+ public StringBuilder AppendFormat(String format, Object arg0, Object arg1, Object arg2)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return AppendFormatHelper(null, format, new ParamsArray(arg0, arg1, arg2));
+ }
+
+ public StringBuilder AppendFormat(String format, params Object[] args)
+ {
+ if (args == null)
+ {
+ // To preserve the original exception behavior, throw an exception about format if both
+ // args and format are null. The actual null check for format is in AppendFormatHelper.
+ throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args));
+ }
+ Contract.Ensures(Contract.Result<String>() != null);
+ Contract.EndContractBlock();
+
+ return AppendFormatHelper(null, format, new ParamsArray(args));
+ }
+
+ public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return AppendFormatHelper(provider, format, new ParamsArray(arg0));
+ }
+
+ public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1));
+ }
+
+ public StringBuilder AppendFormat(IFormatProvider provider, String format, Object arg0, Object arg1, Object arg2)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2));
+ }
+
+ public StringBuilder AppendFormat(IFormatProvider provider, String format, params Object[] args)
+ {
+ if (args == null)
+ {
+ // To preserve the original exception behavior, throw an exception about format if both
+ // args and format are null. The actual null check for format is in AppendFormatHelper.
+ throw new ArgumentNullException((format == null) ? nameof(format) : nameof(args));
+ }
+ Contract.Ensures(Contract.Result<String>() != null);
+ Contract.EndContractBlock();
+
+ return AppendFormatHelper(provider, format, new ParamsArray(args));
+ }
+
+ private static void FormatError()
+ {
+ throw new FormatException(SR.Format_InvalidString);
+ }
+
+ // undocumented exclusive limits on the range for Argument Hole Index and Argument Hole Alignment.
+ private const int Index_Limit = 1000000; // Note: 0 <= ArgIndex < Index_Limit
+ private const int Width_Limit = 1000000; // Note: -Width_Limit < ArgAlign < Width_Limit
+
+ internal StringBuilder AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
+ {
+ if (format == null)
+ {
+ throw new ArgumentNullException(nameof(format));
+ }
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ Contract.EndContractBlock();
+
+ int pos = 0;
+ int len = format.Length;
+ char ch = '\x0';
+ StringBuilder unescapedItemFormat = null;
+
+ ICustomFormatter cf = null;
+ if (provider != null)
+ {
+ cf = (ICustomFormatter)provider.GetFormat(typeof(ICustomFormatter));
+ }
+
+ while (true)
+ {
+ while (pos < len)
+ {
+ ch = format[pos];
+
+ pos++;
+ // Is it a closing brace?
+ if (ch == '}')
+ {
+ // Check next character (if there is one) to see if it is escaped. eg }}
+ if (pos < len && format[pos] == '}')
+ pos++;
+ else
+ // Otherwise treat it as an error (Mismatched closing brace)
+ FormatError();
+ }
+ // Is it a opening brace?
+ if (ch == '{')
+ {
+ // Check next character (if there is one) to see if it is escaped. eg {{
+ if (pos < len && format[pos] == '{')
+ pos++;
+ else
+ {
+ // Otherwise treat it as the opening brace of an Argument Hole.
+ pos--;
+ break;
+ }
+ }
+ // If it neither then treat the character as just text.
+ Append(ch);
+ }
+
+ //
+ // Start of parsing of Argument Hole.
+ // Argument Hole ::= { Index (, WS* Alignment WS*)? (: Formatting)? }
+ //
+ if (pos == len) break;
+
+ //
+ // Start of parsing required Index parameter.
+ // Index ::= ('0'-'9')+ WS*
+ //
+ pos++;
+ // If reached end of text then error (Unexpected end of text)
+ // or character is not a digit then error (Unexpected Character)
+ if (pos == len || (ch = format[pos]) < '0' || ch > '9') FormatError();
+ int index = 0;
+ do
+ {
+ index = index * 10 + ch - '0';
+ pos++;
+ // If reached end of text then error (Unexpected end of text)
+ if (pos == len) FormatError();
+ ch = format[pos];
+ // so long as character is digit and value of the index is less than 1000000 ( index limit )
+ } while (ch >= '0' && ch <= '9' && index < Index_Limit);
+
+ // If value of index is not within the range of the arguments passed in then error (Index out of range)
+ if (index >= args.Length) throw new FormatException(SR.Format_IndexOutOfRange);
+
+ // Consume optional whitespace.
+ while (pos < len && (ch = format[pos]) == ' ') pos++;
+ // End of parsing index parameter.
+
+ //
+ // Start of parsing of optional Alignment
+ // Alignment ::= comma WS* minus? ('0'-'9')+ WS*
+ //
+ bool leftJustify = false;
+ int width = 0;
+ // Is the character a comma, which indicates the start of alignment parameter.
+ if (ch == ',')
+ {
+ pos++;
+
+ // Consume Optional whitespace
+ while (pos < len && format[pos] == ' ') pos++;
+
+ // If reached the end of the text then error (Unexpected end of text)
+ if (pos == len) FormatError();
+
+ // Is there a minus sign?
+ ch = format[pos];
+ if (ch == '-')
+ {
+ // Yes, then alignment is left justified.
+ leftJustify = true;
+ pos++;
+ // If reached end of text then error (Unexpected end of text)
+ if (pos == len) FormatError();
+ ch = format[pos];
+ }
+
+ // If current character is not a digit then error (Unexpected character)
+ if (ch < '0' || ch > '9') FormatError();
+ // Parse alignment digits.
+ do
+ {
+ width = width * 10 + ch - '0';
+ pos++;
+ // If reached end of text then error. (Unexpected end of text)
+ if (pos == len) FormatError();
+ ch = format[pos];
+ // So long a current character is a digit and the value of width is less than 100000 ( width limit )
+ } while (ch >= '0' && ch <= '9' && width < Width_Limit);
+ // end of parsing Argument Alignment
+ }
+
+ // Consume optional whitespace
+ while (pos < len && (ch = format[pos]) == ' ') pos++;
+
+ //
+ // Start of parsing of optional formatting parameter.
+ //
+ Object arg = args[index];
+ String itemFormat = null;
+ // Is current character a colon? which indicates start of formatting parameter.
+ if (ch == ':')
+ {
+ pos++;
+ int startPos = pos;
+
+ while (true)
+ {
+ // If reached end of text then error. (Unexpected end of text)
+ if (pos == len) FormatError();
+ ch = format[pos];
+ pos++;
+
+ // Is character a opening or closing brace?
+ if (ch == '}' || ch == '{')
+ {
+ if (ch == '{')
+ {
+ // Yes, is next character also a opening brace, then treat as escaped. eg {{
+ if (pos < len && format[pos] == '{')
+ pos++;
+ else
+ // Error Argument Holes can not be nested.
+ FormatError();
+ }
+ else
+ {
+ // Yes, is next character also a closing brace, then treat as escaped. eg }}
+ if (pos < len && format[pos] == '}')
+ pos++;
+ else
+ {
+ // No, then treat it as the closing brace of an Arg Hole.
+ pos--;
+ break;
+ }
+ }
+
+ // Reaching here means the brace has been escaped
+ // so we need to build up the format string in segments
+ if (unescapedItemFormat == null)
+ {
+ unescapedItemFormat = new StringBuilder();
+ }
+ unescapedItemFormat.Append(format, startPos, pos - startPos - 1);
+ startPos = pos;
+ }
+ }
+
+ if (unescapedItemFormat == null || unescapedItemFormat.Length == 0)
+ {
+ if (startPos != pos)
+ {
+ // There was no brace escaping, extract the item format as a single string
+ itemFormat = format.Substring(startPos, pos - startPos);
+ }
+ }
+ else
+ {
+ unescapedItemFormat.Append(format, startPos, pos - startPos);
+ itemFormat = unescapedItemFormat.ToString();
+ unescapedItemFormat.Clear();
+ }
+ }
+ // If current character is not a closing brace then error. (Unexpected Character)
+ if (ch != '}') FormatError();
+ // Construct the output for this arg hole.
+ pos++;
+ String s = null;
+ if (cf != null)
+ {
+ s = cf.Format(itemFormat, arg, provider);
+ }
+
+ if (s == null)
+ {
+ IFormattable formattableArg = arg as IFormattable;
+
+ if (formattableArg != null)
+ {
+ s = formattableArg.ToString(itemFormat, provider);
+ }
+ else if (arg != null)
+ {
+ s = arg.ToString();
+ }
+ }
+ // Append it to the final output of the Format String.
+ if (s == null) s = String.Empty;
+ int pad = width - s.Length;
+ if (!leftJustify && pad > 0) Append(' ', pad);
+ Append(s);
+ if (leftJustify && pad > 0) Append(' ', pad);
+ // Continue to parse other characters.
+ }
+ return this;
+ }
+
+ // Returns a reference to the current StringBuilder with all instances of oldString
+ // replaced with newString. If startIndex and count are specified,
+ // we only replace strings completely contained in the range of startIndex to startIndex +
+ // count. The strings to be replaced are checked on an ordinal basis (e.g. not culture aware). If
+ // newValue is null, instances of oldValue are removed (e.g. replaced with nothing.).
+ //
+ public StringBuilder Replace(String oldValue, String newValue)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+ return Replace(oldValue, newValue, 0, Length);
+ }
+
+ public bool Equals(StringBuilder sb)
+ {
+ if (sb == null)
+ return false;
+ if (Capacity != sb.Capacity || MaxCapacity != sb.MaxCapacity || Length != sb.Length)
+ return false;
+ if (sb == this)
+ return true;
+
+ StringBuilder thisChunk = this;
+ int thisChunkIndex = thisChunk.m_ChunkLength;
+ StringBuilder sbChunk = sb;
+ int sbChunkIndex = sbChunk.m_ChunkLength;
+ for (;;)
+ {
+ // Decrement the pointer to the 'this' StringBuilder
+ --thisChunkIndex;
+ --sbChunkIndex;
+
+ while (thisChunkIndex < 0)
+ {
+ thisChunk = thisChunk.m_ChunkPrevious;
+ if (thisChunk == null)
+ break;
+ thisChunkIndex = thisChunk.m_ChunkLength + thisChunkIndex;
+ }
+
+ // Decrement the pointer to the 'this' StringBuilder
+ while (sbChunkIndex < 0)
+ {
+ sbChunk = sbChunk.m_ChunkPrevious;
+ if (sbChunk == null)
+ break;
+ sbChunkIndex = sbChunk.m_ChunkLength + sbChunkIndex;
+ }
+
+ if (thisChunkIndex < 0)
+ return sbChunkIndex < 0;
+ if (sbChunkIndex < 0)
+ return false;
+ if (thisChunk.m_ChunkChars[thisChunkIndex] != sbChunk.m_ChunkChars[sbChunkIndex])
+ return false;
+ }
+ }
+
+ public StringBuilder Replace(String oldValue, String newValue, int startIndex, int count)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ int currentLength = Length;
+ if ((uint)startIndex > (uint)currentLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+ }
+ if (count < 0 || startIndex > currentLength - count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Index);
+ }
+ if (oldValue == null)
+ {
+ throw new ArgumentNullException(nameof(oldValue));
+ }
+ if (oldValue.Length == 0)
+ {
+ throw new ArgumentException(SR.Argument_EmptyName, nameof(oldValue));
+ }
+
+ if (newValue == null)
+ newValue = "";
+
+ int deltaLength = newValue.Length - oldValue.Length;
+
+ int[] replacements = null; // A list of replacement positions in a chunk to apply
+ int replacementsCount = 0;
+
+ // Find the chunk, indexInChunk for the starting point
+ StringBuilder chunk = FindChunkForIndex(startIndex);
+ int indexInChunk = startIndex - chunk.m_ChunkOffset;
+ while (count > 0)
+ {
+ // Look for a match in the chunk,indexInChunk pointer
+ if (StartsWith(chunk, indexInChunk, count, oldValue))
+ {
+ // Push it on my replacements array (with growth), we will do all replacements in a
+ // given chunk in one operation below (see ReplaceAllInChunk) so we don't have to slide
+ // many times.
+ if (replacements == null)
+ replacements = new int[5];
+ else if (replacementsCount >= replacements.Length)
+ {
+ Array.Resize(ref replacements, replacements.Length * 3 / 2 + 4); // grow by 1.5X but more in the beginning
+ }
+ replacements[replacementsCount++] = indexInChunk;
+ indexInChunk += oldValue.Length;
+ count -= oldValue.Length;
+ }
+ else
+ {
+ indexInChunk++;
+ --count;
+ }
+
+ if (indexInChunk >= chunk.m_ChunkLength || count == 0) // Have we moved out of the current chunk
+ {
+ // Replacing mutates the blocks, so we need to convert to logical index and back afterward.
+ int index = indexInChunk + chunk.m_ChunkOffset;
+ int indexBeforeAdjustment = index;
+
+ // See if we accumulated any replacements, if so apply them
+ ReplaceAllInChunk(replacements, replacementsCount, chunk, oldValue.Length, newValue);
+ // The replacement has affected the logical index. Adjust it.
+ index += ((newValue.Length - oldValue.Length) * replacementsCount);
+ replacementsCount = 0;
+
+ chunk = FindChunkForIndex(index);
+ indexInChunk = index - chunk.m_ChunkOffset;
+ Debug.Assert(chunk != null || count == 0, "Chunks ended prematurely");
+ }
+ }
+ VerifyClassInvariant();
+ return this;
+ }
+
+ // Returns a StringBuilder with all instances of oldChar replaced with
+ // newChar. The size of the StringBuilder is unchanged because we're only
+ // replacing characters. If startIndex and count are specified, we
+ // only replace characters in the range from startIndex to startIndex+count
+ //
+ public StringBuilder Replace(char oldChar, char newChar)
+ {
+ return Replace(oldChar, newChar, 0, Length);
+ }
+ public StringBuilder Replace(char oldChar, char newChar, int startIndex, int count)
+ {
+ Contract.Ensures(Contract.Result<StringBuilder>() != null);
+
+ int currentLength = Length;
+ if ((uint)startIndex > (uint)currentLength)
+ {
+ throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
+ }
+
+ if (count < 0 || startIndex > currentLength - count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_Index);
+ }
+
+ int endIndex = startIndex + count;
+ StringBuilder chunk = this;
+ for (;;)
+ {
+ int endIndexInChunk = endIndex - chunk.m_ChunkOffset;
+ int startIndexInChunk = startIndex - chunk.m_ChunkOffset;
+ if (endIndexInChunk >= 0)
+ {
+ int curInChunk = Math.Max(startIndexInChunk, 0);
+ int endInChunk = Math.Min(chunk.m_ChunkLength, endIndexInChunk);
+ while (curInChunk < endInChunk)
+ {
+ if (chunk.m_ChunkChars[curInChunk] == oldChar)
+ chunk.m_ChunkChars[curInChunk] = newChar;
+ curInChunk++;
+ }
+ }
+ if (startIndexInChunk >= 0)
+ break;
+ chunk = chunk.m_ChunkPrevious;
+ }
+ return this;
+ }
+
+ /// <summary>
+ /// Appends 'value' of length 'count' to the stringBuilder.
+ /// </summary>
+ [CLSCompliant(false)]
+ public unsafe StringBuilder Append(char* value, int valueCount)
+ {
+ // We don't check null value as this case will throw null reference exception anyway
+ if (valueCount < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(valueCount), SR.ArgumentOutOfRange_NegativeCount);
+ }
+
+ // this is where we can check if the valueCount will put us over m_MaxCapacity
+ // We are doing the check here to prevent the corruption of the StringBuilder.
+ int newLength = Length + valueCount;
+ if (newLength > m_MaxCapacity || newLength < valueCount)
+ {
+ throw new ArgumentOutOfRangeException(nameof(valueCount), SR.ArgumentOutOfRange_LengthGreaterThanCapacity);
+ }
+
+ // This case is so common we want to optimize for it heavily.
+ int newIndex = valueCount + m_ChunkLength;
+ if (newIndex <= m_ChunkChars.Length)
+ {
+ ThreadSafeCopy(value, m_ChunkChars, m_ChunkLength, valueCount);
+ m_ChunkLength = newIndex;
+ }
+ else
+ {
+ // Copy the first chunk
+ int firstLength = m_ChunkChars.Length - m_ChunkLength;
+ if (firstLength > 0)
+ {
+ ThreadSafeCopy(value, m_ChunkChars, m_ChunkLength, firstLength);
+ m_ChunkLength = m_ChunkChars.Length;
+ }
+
+ // Expand the builder to add another chunk.
+ int restLength = valueCount - firstLength;
+ ExpandByABlock(restLength);
+ Debug.Assert(m_ChunkLength == 0, "Expand did not make a new block");
+
+ // Copy the second chunk
+ ThreadSafeCopy(value + firstLength, m_ChunkChars, 0, restLength);
+ m_ChunkLength = restLength;
+ }
+ VerifyClassInvariant();
+ return this;
+ }
+
+ /// <summary>
+ /// Inserts 'value' of length 'cou
+ /// </summary>
+ unsafe private void Insert(int index, char* value, int valueCount)
+ {
+ if ((uint)index > (uint)Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index), SR.ArgumentOutOfRange_Index);
+ }
+
+ if (valueCount > 0)
+ {
+ StringBuilder chunk;
+ int indexInChunk;
+ MakeRoom(index, valueCount, out chunk, out indexInChunk, false);
+ ReplaceInPlaceAtChunk(ref chunk, ref indexInChunk, value, valueCount);
+ }
+ }
+
+ /// <summary>
+ /// 'replacements' is a list of index (relative to the begining of the 'chunk' to remove
+ /// 'removeCount' characters and replace them with 'value'. This routine does all those
+ /// replacements in bulk (and therefore very efficiently.
+ /// with the string 'value'.
+ /// </summary>
+ private void ReplaceAllInChunk(int[] replacements, int replacementsCount, StringBuilder sourceChunk, int removeCount, string value)
+ {
+ if (replacementsCount <= 0)
+ return;
+
+ unsafe
+ {
+ fixed (char* valuePtr = value)
+ {
+ // calculate the total amount of extra space or space needed for all the replacements.
+ int delta = (value.Length - removeCount) * replacementsCount;
+
+ StringBuilder targetChunk = sourceChunk; // the target as we copy chars down
+ int targetIndexInChunk = replacements[0];
+
+ // Make the room needed for all the new characters if needed.
+ if (delta > 0)
+ MakeRoom(targetChunk.m_ChunkOffset + targetIndexInChunk, delta, out targetChunk, out targetIndexInChunk, true);
+ // We made certain that characters after the insertion point are not moved,
+ int i = 0;
+ for (;;)
+ {
+ // Copy in the new string for the ith replacement
+ ReplaceInPlaceAtChunk(ref targetChunk, ref targetIndexInChunk, valuePtr, value.Length);
+ int gapStart = replacements[i] + removeCount;
+ i++;
+ if (i >= replacementsCount)
+ break;
+
+ int gapEnd = replacements[i];
+ Debug.Assert(gapStart < sourceChunk.m_ChunkChars.Length, "gap starts at end of buffer. Should not happen");
+ Debug.Assert(gapStart <= gapEnd, "negative gap size");
+ Debug.Assert(gapEnd <= sourceChunk.m_ChunkLength, "gap too big");
+ if (delta != 0) // can skip the sliding of gaps if source an target string are the same size.
+ {
+ // Copy the gap data between the current replacement and the the next replacement
+ fixed (char* sourcePtr = &sourceChunk.m_ChunkChars[gapStart])
+ ReplaceInPlaceAtChunk(ref targetChunk, ref targetIndexInChunk, sourcePtr, gapEnd - gapStart);
+ }
+ else
+ {
+ targetIndexInChunk += gapEnd - gapStart;
+ Debug.Assert(targetIndexInChunk <= targetChunk.m_ChunkLength, "gap not in chunk");
+ }
+ }
+
+ // Remove extra space if necessary.
+ if (delta < 0)
+ Remove(targetChunk.m_ChunkOffset + targetIndexInChunk, -delta, out targetChunk, out targetIndexInChunk);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns true if the string that is starts at 'chunk' and 'indexInChunk, and has a logical
+ /// length of 'count' starts with the string 'value'.
+ /// </summary>
+ private bool StartsWith(StringBuilder chunk, int indexInChunk, int count, string value)
+ {
+ for (int i = 0; i < value.Length; i++)
+ {
+ if (count == 0)
+ return false;
+ if (indexInChunk >= chunk.m_ChunkLength)
+ {
+ chunk = Next(chunk);
+ if (chunk == null)
+ return false;
+ indexInChunk = 0;
+ }
+
+ // See if there no match, break out of the inner for loop
+ if (value[i] != chunk.m_ChunkChars[indexInChunk])
+ return false;
+
+ indexInChunk++;
+ --count;
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// ReplaceInPlaceAtChunk is the logical equivalent of 'memcpy'. Given a chunk and ann index in
+ /// that chunk, it copies in 'count' characters from 'value' and updates 'chunk, and indexInChunk to
+ /// point at the end of the characters just copyied (thus you can splice in strings from multiple
+ /// places by calling this mulitple times.
+ /// </summary>
+ unsafe private void ReplaceInPlaceAtChunk(ref StringBuilder chunk, ref int indexInChunk, char* value, int count)
+ {
+ if (count != 0)
+ {
+ for (;;)
+ {
+ int lengthInChunk = chunk.m_ChunkLength - indexInChunk;
+ Debug.Assert(lengthInChunk >= 0, "index not in chunk");
+
+ int lengthToCopy = Math.Min(lengthInChunk, count);
+ ThreadSafeCopy(value, chunk.m_ChunkChars, indexInChunk, lengthToCopy);
+
+ // Advance the index.
+ indexInChunk += lengthToCopy;
+ if (indexInChunk >= chunk.m_ChunkLength)
+ {
+ chunk = Next(chunk);
+ indexInChunk = 0;
+ }
+ count -= lengthToCopy;
+ if (count == 0)
+ break;
+ value += lengthToCopy;
+ }
+ }
+ }
+
+ /// <summary>
+ /// We have to prevent modification off the end of an array.
+ /// The only way to do this is to copy all interesting variables out of the heap and then do the
+ /// bounds check. This is what we do here.
+ /// </summary>
+ private static unsafe void ThreadSafeCopy(char* sourcePtr, char[] destination, int destinationIndex, int count)
+ {
+ if (count > 0)
+ {
+ if ((uint)destinationIndex <= (uint)destination.Length && (destinationIndex + count) <= destination.Length)
+ {
+ fixed (char* destinationPtr = &destination[destinationIndex])
+ string.wstrcpy(destinationPtr, sourcePtr, count);
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_Index);
+ }
+ }
+ }
+
+ private static void ThreadSafeCopy(char[] source, int sourceIndex, char[] destination, int destinationIndex, int count)
+ {
+ if (count > 0)
+ {
+ if ((uint)sourceIndex <= (uint)source.Length && (sourceIndex + count) <= source.Length)
+ {
+ unsafe
+ {
+ fixed (char* sourcePtr = &source[sourceIndex])
+ ThreadSafeCopy(sourcePtr, destination, destinationIndex, count);
+ }
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Finds the chunk for the logical index (number of characters in the whole stringbuilder) 'index'
+ /// YOu can then get the offset in this chunk by subtracting the m_BlockOffset field from 'index'
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ private StringBuilder FindChunkForIndex(int index)
+ {
+ Debug.Assert(0 <= index && index <= Length, "index not in string");
+
+ StringBuilder ret = this;
+ while (ret.m_ChunkOffset > index)
+ ret = ret.m_ChunkPrevious;
+
+ Debug.Assert(ret != null, "index not in string");
+ return ret;
+ }
+
+ /// <summary>
+ /// Finds the chunk for the logical byte index 'byteIndex'
+ /// </summary>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ private StringBuilder FindChunkForByte(int byteIndex)
+ {
+ Debug.Assert(0 <= byteIndex && byteIndex <= Length * sizeof(char), "Byte Index not in string");
+
+ StringBuilder ret = this;
+ while (ret.m_ChunkOffset * sizeof(char) > byteIndex)
+ ret = ret.m_ChunkPrevious;
+
+ Debug.Assert(ret != null, "Byte Index not in string");
+ return ret;
+ }
+
+ /// <summary>
+ /// Finds the chunk that logically follows the 'chunk' chunk. Chunks only persist the pointer to
+ /// the chunk that is logically before it, so this routine has to start at the this pointer (which
+ /// is a assumed to point at the chunk representing the whole stringbuilder) and search
+ /// until it finds the current chunk (thus is O(n)). So it is more expensive than a field fetch!
+ /// </summary>
+ private StringBuilder Next(StringBuilder chunk)
+ {
+ if (chunk == this)
+ return null;
+ return FindChunkForIndex(chunk.m_ChunkOffset + chunk.m_ChunkLength);
+ }
+
+ /// <summary>
+ /// Assumes that 'this' is the last chunk in the list and that it is full. Upon return the 'this'
+ /// block is updated so that it is a new block that has at least 'minBlockCharCount' characters.
+ /// that can be used to copy characters into it.
+ /// </summary>
+ private void ExpandByABlock(int minBlockCharCount)
+ {
+ Contract.Requires(Capacity == Length, "Expand expect to be called only when there is no space left"); // We are currently full
+ Contract.Requires(minBlockCharCount > 0, "Expansion request must be positive");
+
+ VerifyClassInvariant();
+
+ if ((minBlockCharCount + Length) > m_MaxCapacity || minBlockCharCount + Length < minBlockCharCount)
+ throw new ArgumentOutOfRangeException("requiredLength", SR.ArgumentOutOfRange_SmallCapacity);
+
+ // Compute the length of the new block we need
+ // We make the new chunk at least big enough for the current need (minBlockCharCount)
+ // But also as big as the current length (thus doubling capacity), up to a maximum
+ // (so we stay in the small object heap, and never allocate really big chunks even if
+ // the string gets really big.
+ int newBlockLength = Math.Max(minBlockCharCount, Math.Min(Length, MaxChunkSize));
+
+ // Copy the current block to the new block, and initialize this to point at the new buffer.
+ m_ChunkPrevious = new StringBuilder(this);
+ m_ChunkOffset += m_ChunkLength;
+ m_ChunkLength = 0;
+
+ // Check for integer overflow (logical buffer size > int.MaxInt)
+ if (m_ChunkOffset + newBlockLength < newBlockLength)
+ {
+ m_ChunkChars = null;
+ throw new OutOfMemoryException();
+ }
+ m_ChunkChars = new char[newBlockLength];
+
+ VerifyClassInvariant();
+ }
+
+ /// <summary>
+ /// Used by ExpandByABlock to create a new chunk. The new chunk is a copied from 'from'
+ /// In particular the buffer is shared. It is expected that 'from' chunk (which represents
+ /// the whole list, is then updated to point to point to this new chunk.
+ /// </summary>
+ private StringBuilder(StringBuilder from)
+ {
+ m_ChunkLength = from.m_ChunkLength;
+ m_ChunkOffset = from.m_ChunkOffset;
+ m_ChunkChars = from.m_ChunkChars;
+ m_ChunkPrevious = from.m_ChunkPrevious;
+ m_MaxCapacity = from.m_MaxCapacity;
+ VerifyClassInvariant();
+ }
+
+ /// <summary>
+ /// Creates a gap of size 'count' at the logical offset (count of characters in the whole string
+ /// builder) 'index'. It returns the 'chunk' and 'indexInChunk' which represents a pointer to
+ /// this gap that was just created. You can then use 'ReplaceInPlaceAtChunk' to fill in the
+ /// chunk
+ ///
+ /// ReplaceAllChunks relies on the fact that indexes above 'index' are NOT moved outside 'chunk'
+ /// by this process (because we make the space by creating the cap BEFORE the chunk). If we
+ /// change this ReplaceAllChunks needs to be updated.
+ ///
+ /// If dontMoveFollowingChars is true, then the room must be made by inserting a chunk BEFORE the
+ /// current chunk (this is what it does most of the time anyway)
+ /// </summary>
+ private void MakeRoom(int index, int count, out StringBuilder chunk, out int indexInChunk, bool doneMoveFollowingChars)
+ {
+ VerifyClassInvariant();
+ Debug.Assert(count > 0, "Count must be strictly positive");
+ Debug.Assert(index >= 0, "Index can't be negative");
+ if (count + Length > m_MaxCapacity || count + Length < count)
+ throw new ArgumentOutOfRangeException("requiredLength", SR.ArgumentOutOfRange_SmallCapacity);
+
+ chunk = this;
+ while (chunk.m_ChunkOffset > index)
+ {
+ chunk.m_ChunkOffset += count;
+ chunk = chunk.m_ChunkPrevious;
+ }
+ indexInChunk = index - chunk.m_ChunkOffset;
+
+ // Cool, we have some space in this block, and you don't have to copy much to get it, go ahead
+ // and use it. This happens typically when you repeatedly insert small strings at a spot
+ // (typically the absolute front) of the buffer.
+ if (!doneMoveFollowingChars && chunk.m_ChunkLength <= DefaultCapacity * 2 && chunk.m_ChunkChars.Length - chunk.m_ChunkLength >= count)
+ {
+ for (int i = chunk.m_ChunkLength; i > indexInChunk;)
+ {
+ --i;
+ chunk.m_ChunkChars[i + count] = chunk.m_ChunkChars[i];
+ }
+ chunk.m_ChunkLength += count;
+ return;
+ }
+
+ // Allocate space for the new chunk (will go before this one)
+ StringBuilder newChunk = new StringBuilder(Math.Max(count, DefaultCapacity), chunk.m_MaxCapacity, chunk.m_ChunkPrevious);
+ newChunk.m_ChunkLength = count;
+
+ // Copy the head of the buffer to the new buffer.
+ int copyCount1 = Math.Min(count, indexInChunk);
+ if (copyCount1 > 0)
+ {
+ unsafe
+ {
+ fixed (char* chunkCharsPtr = &chunk.m_ChunkChars[0])
+ {
+ ThreadSafeCopy(chunkCharsPtr, newChunk.m_ChunkChars, 0, copyCount1);
+
+ // Slide characters in the current buffer over to make room.
+ int copyCount2 = indexInChunk - copyCount1;
+ if (copyCount2 >= 0)
+ {
+ ThreadSafeCopy(chunkCharsPtr + copyCount1, chunk.m_ChunkChars, 0, copyCount2);
+ indexInChunk = copyCount2;
+ }
+ }
+ }
+ }
+
+ chunk.m_ChunkPrevious = newChunk; // Wire in the new chunk
+ chunk.m_ChunkOffset += count;
+ if (copyCount1 < count)
+ {
+ chunk = newChunk;
+ indexInChunk = copyCount1;
+ }
+
+ VerifyClassInvariant();
+ }
+
+ /// <summary>
+ /// Used by MakeRoom to allocate another chunk.
+ /// </summary>
+ private StringBuilder(int size, int maxCapacity, StringBuilder previousBlock)
+ {
+ Debug.Assert(size > 0, "size not positive");
+ Debug.Assert(maxCapacity > 0, "maxCapacity not positive");
+ m_ChunkChars = new char[size];
+ m_MaxCapacity = maxCapacity;
+ m_ChunkPrevious = previousBlock;
+ if (previousBlock != null)
+ m_ChunkOffset = previousBlock.m_ChunkOffset + previousBlock.m_ChunkLength;
+ VerifyClassInvariant();
+ }
+
+ /// <summary>
+ /// Removes 'count' characters from the logical index 'startIndex' and returns the chunk and
+ /// index in the chunk of that logical index in the out parameters.
+ /// </summary>
+ private void Remove(int startIndex, int count, out StringBuilder chunk, out int indexInChunk)
+ {
+ VerifyClassInvariant();
+ Debug.Assert(startIndex >= 0 && startIndex < Length, "startIndex not in string");
+
+ int endIndex = startIndex + count;
+
+ // Find the chunks for the start and end of the block to delete.
+ chunk = this;
+ StringBuilder endChunk = null;
+ int endIndexInChunk = 0;
+ for (;;)
+ {
+ if (endIndex - chunk.m_ChunkOffset >= 0)
+ {
+ if (endChunk == null)
+ {
+ endChunk = chunk;
+ endIndexInChunk = endIndex - endChunk.m_ChunkOffset;
+ }
+ if (startIndex - chunk.m_ChunkOffset >= 0)
+ {
+ indexInChunk = startIndex - chunk.m_ChunkOffset;
+ break;
+ }
+ }
+ else
+ {
+ chunk.m_ChunkOffset -= count;
+ }
+ chunk = chunk.m_ChunkPrevious;
+ }
+ Debug.Assert(chunk != null, "fell off beginning of string!");
+
+ int copyTargetIndexInChunk = indexInChunk;
+ int copyCount = endChunk.m_ChunkLength - endIndexInChunk;
+ if (endChunk != chunk)
+ {
+ copyTargetIndexInChunk = 0;
+ // Remove the characters after startIndex to end of the chunk
+ chunk.m_ChunkLength = indexInChunk;
+
+ // Remove the characters in chunks between start and end chunk
+ endChunk.m_ChunkPrevious = chunk;
+ endChunk.m_ChunkOffset = chunk.m_ChunkOffset + chunk.m_ChunkLength;
+
+ // If the start is 0 then we can throw away the whole start chunk
+ if (indexInChunk == 0)
+ {
+ endChunk.m_ChunkPrevious = chunk.m_ChunkPrevious;
+ chunk = endChunk;
+ }
+ }
+ endChunk.m_ChunkLength -= (endIndexInChunk - copyTargetIndexInChunk);
+
+ // SafeCritical: We ensure that endIndexInChunk + copyCount is within range of m_ChunkChars and
+ // also ensure that copyTargetIndexInChunk + copyCount is within the chunk
+ //
+ // Remove any characters in the end chunk, by sliding the characters down.
+ if (copyTargetIndexInChunk != endIndexInChunk) // Sometimes no move is necessary
+ ThreadSafeCopy(endChunk.m_ChunkChars, endIndexInChunk, endChunk.m_ChunkChars, copyTargetIndexInChunk, copyCount);
+
+ Debug.Assert(chunk != null, "fell off beginning of string!");
+ VerifyClassInvariant();
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/UTF32Encoding.cs b/src/mscorlib/shared/System/Text/UTF32Encoding.cs
new file mode 100644
index 0000000000..e4cd6c960e
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/UTF32Encoding.cs
@@ -0,0 +1,1234 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+// Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused.
+//
+
+using System;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+
+namespace System.Text
+{
+ // Encodes text into and out of UTF-32. UTF-32 is a way of writing
+ // Unicode characters with a single storage unit (32 bits) per character,
+ //
+ // The UTF-32 byte order mark is simply the Unicode byte order mark
+ // (0x00FEFF) written in UTF-32 (0x0000FEFF or 0xFFFE0000). The byte order
+ // mark is used mostly to distinguish UTF-32 text from other encodings, and doesn't
+ // switch the byte orderings.
+
+ [Serializable]
+ public sealed class UTF32Encoding : Encoding
+ {
+ /*
+ words bits UTF-32 representation
+ ----- ---- -----------------------------------
+ 1 16 00000000 00000000 xxxxxxxx xxxxxxxx
+ 2 21 00000000 000xxxxx hhhhhhll llllllll
+ ----- ---- -----------------------------------
+
+ Surrogate:
+ Real Unicode value = (HighSurrogate - 0xD800) * 0x400 + (LowSurrogate - 0xDC00) + 0x10000
+ */
+
+ // Used by Encoding.UTF32/BigEndianUTF32 for lazy initialization
+ // The initialization code will not be run until a static member of the class is referenced
+ internal static readonly UTF32Encoding s_default = new UTF32Encoding(bigEndian: false, byteOrderMark: true);
+ internal static readonly UTF32Encoding s_bigEndianDefault = new UTF32Encoding(bigEndian: true, byteOrderMark: true);
+
+ private bool _emitUTF32ByteOrderMark = false;
+ private bool _isThrowException = false;
+ private bool _bigEndian = false;
+
+
+ public UTF32Encoding() : this(false, true, false)
+ {
+ }
+
+
+ public UTF32Encoding(bool bigEndian, bool byteOrderMark) :
+ this(bigEndian, byteOrderMark, false)
+ {
+ }
+
+
+ public UTF32Encoding(bool bigEndian, bool byteOrderMark, bool throwOnInvalidCharacters) :
+ base(bigEndian ? 12001 : 12000)
+ {
+ _bigEndian = bigEndian;
+ _emitUTF32ByteOrderMark = byteOrderMark;
+ _isThrowException = throwOnInvalidCharacters;
+
+ // Encoding constructor already did this, but it'll be wrong if we're throwing exceptions
+ if (_isThrowException)
+ SetDefaultFallbacks();
+ }
+
+ internal override void SetDefaultFallbacks()
+ {
+ // For UTF-X encodings, we use a replacement fallback with an empty string
+ if (_isThrowException)
+ {
+ this.encoderFallback = EncoderFallback.ExceptionFallback;
+ this.decoderFallback = DecoderFallback.ExceptionFallback;
+ }
+ else
+ {
+ this.encoderFallback = new EncoderReplacementFallback("\xFFFD");
+ this.decoderFallback = new DecoderReplacementFallback("\xFFFD");
+ }
+ }
+
+
+ // The following methods are copied from EncodingNLS.cs.
+ // Unfortunately EncodingNLS.cs is internal and we're public, so we have to reimpliment them here.
+ // These should be kept in sync for the following classes:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ // Returns the number of bytes required to encode a range of characters in
+ // a character array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetByteCount(char[] chars, int index, int count)
+ {
+ // Validate input parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - index < count)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input, return 0, avoid fixed empty array problem
+ if (count == 0)
+ return 0;
+
+ // Just call the pointer version
+ fixed (char* pChars = chars)
+ return GetByteCount(pChars + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetByteCount(String s)
+ {
+ // Validate input
+ if (s==null)
+ throw new ArgumentNullException("s");
+ Contract.EndContractBlock();
+
+ fixed (char* pChars = s)
+ return GetByteCount(pChars, s.Length, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetByteCount(char* chars, int count)
+ {
+ // Validate Parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Call it with empty encoder
+ return GetByteCount(chars, count, null);
+ }
+
+ // Parent method is safe.
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ public override unsafe int GetBytes(String s, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ if (s == null || bytes == null)
+ throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (s.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fix our input array if 0 length because fixed doesn't like 0 length arrays
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0])
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // Encodes a range of characters in a character array into a range of bytes
+ // in a byte array. An exception occurs if the byte array is not large
+ // enough to hold the complete encoding of the characters. The
+ // GetByteCount method can be used to determine the exact number of
+ // bytes that will be produced for a given range of characters.
+ // Alternatively, the GetMaxByteCount method can be used to
+ // determine the maximum number of bytes that will be produced for a given
+ // number of characters, regardless of the actual character values.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ // Validate parameters
+ if (chars == null || bytes == null)
+ throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If nothing to encode return 0, avoid fixed problem
+ if (charCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fix our input array if 0 length because fixed doesn't like 0 length arrays
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0])
+ // Remember that byteCount is # to decode, not size of array.
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetBytes(chars, charCount, bytes, byteCount, null);
+ }
+
+ // Returns the number of characters produced by decoding a range of bytes
+ // in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetCharCount(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - index < count)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input just return 0, fixed doesn't like 0 length arrays.
+ if (count == 0)
+ return 0;
+
+ // Just call pointer version
+ fixed (byte* pBytes = bytes)
+ return GetCharCount(pBytes + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetCharCount(byte* bytes, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetCharCount(bytes, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
+ char[] chars, int charIndex)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (byteIndex < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if ( bytes.Length - byteIndex < byteCount)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (charIndex < 0 || charIndex > chars.Length)
+ throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If no input, return 0 & avoid fixed problem
+ if (byteCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int charCount = chars.Length - charIndex;
+
+ // Fix our input array if 0 length because fixed doesn't like 0 length arrays
+ if (chars.Length == 0)
+ chars = new char[1];
+
+ fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0])
+ // Remember that charCount is # to decode, not size of array
+ return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetChars(bytes, byteCount, chars, charCount, null);
+ }
+
+ // Returns a string containing the decoded representation of a range of
+ // bytes in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe String GetString(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - index < count)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // Avoid problems with empty input buffer
+ if (count == 0) return String.Empty;
+
+ fixed (byte* pBytes = bytes)
+ return String.CreateStringFromEncoding(
+ pBytes + index, count, this);
+ }
+
+ //
+ // End of standard methods copied from EncodingNLS.cs
+ //
+
+ internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder)
+ {
+ Debug.Assert(chars != null, "[UTF32Encoding.GetByteCount]chars!=null");
+ Debug.Assert(count >= 0, "[UTF32Encoding.GetByteCount]count >=0");
+
+ char* end = chars + count;
+ char* charStart = chars;
+ int byteCount = 0;
+
+ char highSurrogate = '\0';
+
+ // For fallback we may need a fallback buffer
+ EncoderFallbackBuffer fallbackBuffer = null;
+ char* charsForFallback;
+
+ if (encoder != null)
+ {
+ highSurrogate = encoder.charLeftOver;
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // We mustn't have left over fallback data when counting
+ if (fallbackBuffer.Remaining > 0)
+ throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
+ }
+ else
+ {
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ }
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, end, encoder, false);
+
+ char ch;
+ TryAgain:
+
+ while (((ch = fallbackBuffer.InternalGetNextChar()) != 0) || chars < end)
+ {
+ // First unwind any fallback
+ if (ch == 0)
+ {
+ // No fallback, just get next char
+ ch = *chars;
+ chars++;
+ }
+
+ // Do we need a low surrogate?
+ if (highSurrogate != '\0')
+ {
+ //
+ // In previous char, we encounter a high surrogate, so we are expecting a low surrogate here.
+ //
+ if (Char.IsLowSurrogate(ch))
+ {
+ // They're all legal
+ highSurrogate = '\0';
+
+ //
+ // One surrogate pair will be translated into 4 bytes UTF32.
+ //
+
+ byteCount += 4;
+ continue;
+ }
+
+ // We are missing our low surrogate, decrement chars and fallback the high surrogate
+ // The high surrogate may have come from the encoder, but nothing else did.
+ Debug.Assert(chars > charStart,
+ "[UTF32Encoding.GetByteCount]Expected chars to have advanced if no low surrogate");
+ chars--;
+
+ // Do the fallback
+ charsForFallback = chars;
+ fallbackBuffer.InternalFallback(highSurrogate, ref charsForFallback);
+ chars = charsForFallback;
+
+ // We're going to fallback the old high surrogate.
+ highSurrogate = '\0';
+ continue;
+ }
+
+ // Do we have another high surrogate?
+ if (Char.IsHighSurrogate(ch))
+ {
+ //
+ // We'll have a high surrogate to check next time.
+ //
+ highSurrogate = ch;
+ continue;
+ }
+
+ // Check for illegal characters
+ if (Char.IsLowSurrogate(ch))
+ {
+ // We have a leading low surrogate, do the fallback
+ charsForFallback = chars;
+ fallbackBuffer.InternalFallback(ch, ref charsForFallback);
+ chars = charsForFallback;
+
+ // Try again with fallback buffer
+ continue;
+ }
+
+ // We get to add the character (4 bytes UTF32)
+ byteCount += 4;
+ }
+
+ // May have to do our last surrogate
+ if ((encoder == null || encoder.MustFlush) && highSurrogate > 0)
+ {
+ // We have to do the fallback for the lonely high surrogate
+ charsForFallback = chars;
+ fallbackBuffer.InternalFallback(highSurrogate, ref charsForFallback);
+ chars = charsForFallback;
+
+ highSurrogate = (char)0;
+ goto TryAgain;
+ }
+
+ // Check for overflows.
+ if (byteCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow);
+
+ // Shouldn't have anything in fallback buffer for GetByteCount
+ // (don't have to check m_throwOnOverflow for count)
+ Debug.Assert(fallbackBuffer.Remaining == 0,
+ "[UTF32Encoding.GetByteCount]Expected empty fallback buffer at end");
+
+ // Return our count
+ return byteCount;
+ }
+
+ internal override unsafe int GetBytes(char* chars, int charCount,
+ byte* bytes, int byteCount, EncoderNLS encoder)
+ {
+ Debug.Assert(chars != null, "[UTF32Encoding.GetBytes]chars!=null");
+ Debug.Assert(bytes != null, "[UTF32Encoding.GetBytes]bytes!=null");
+ Debug.Assert(byteCount >= 0, "[UTF32Encoding.GetBytes]byteCount >=0");
+ Debug.Assert(charCount >= 0, "[UTF32Encoding.GetBytes]charCount >=0");
+
+ char* charStart = chars;
+ char* charEnd = chars + charCount;
+ byte* byteStart = bytes;
+ byte* byteEnd = bytes + byteCount;
+
+ char highSurrogate = '\0';
+
+ // For fallback we may need a fallback buffer
+ EncoderFallbackBuffer fallbackBuffer = null;
+ char* charsForFallback;
+
+ if (encoder != null)
+ {
+ highSurrogate = encoder.charLeftOver;
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // We mustn't have left over fallback data when not converting
+ if (encoder.m_throwOnOverflow && fallbackBuffer.Remaining > 0)
+ throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
+ }
+ else
+ {
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ }
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
+
+ char ch;
+ TryAgain:
+
+ while (((ch = fallbackBuffer.InternalGetNextChar()) != 0) || chars < charEnd)
+ {
+ // First unwind any fallback
+ if (ch == 0)
+ {
+ // No fallback, just get next char
+ ch = *chars;
+ chars++;
+ }
+
+ // Do we need a low surrogate?
+ if (highSurrogate != '\0')
+ {
+ //
+ // In previous char, we encountered a high surrogate, so we are expecting a low surrogate here.
+ //
+ if (Char.IsLowSurrogate(ch))
+ {
+ // Is it a legal one?
+ uint iTemp = GetSurrogate(highSurrogate, ch);
+ highSurrogate = '\0';
+
+ //
+ // One surrogate pair will be translated into 4 bytes UTF32.
+ //
+ if (bytes + 3 >= byteEnd)
+ {
+ // Don't have 4 bytes
+ if (fallbackBuffer.bFallingBack)
+ {
+ fallbackBuffer.MovePrevious(); // Aren't using these 2 fallback chars
+ fallbackBuffer.MovePrevious();
+ }
+ else
+ {
+ // If we don't have enough room, then either we should've advanced a while
+ // or we should have bytes==byteStart and throw below
+ Debug.Assert(chars > charStart + 1 || bytes == byteStart,
+ "[UnicodeEncoding.GetBytes]Expected chars to have when no room to add surrogate pair");
+ chars -= 2; // Aren't using those 2 chars
+ }
+ ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written)
+ highSurrogate = (char)0; // Nothing left over (we backed up to start of pair if supplimentary)
+ break;
+ }
+
+ if (_bigEndian)
+ {
+ *(bytes++) = (byte)(0x00);
+ *(bytes++) = (byte)(iTemp >> 16); // Implies & 0xFF, which isn't needed cause high are all 0
+ *(bytes++) = (byte)(iTemp >> 8); // Implies & 0xFF
+ *(bytes++) = (byte)(iTemp); // Implies & 0xFF
+ }
+ else
+ {
+ *(bytes++) = (byte)(iTemp); // Implies & 0xFF
+ *(bytes++) = (byte)(iTemp >> 8); // Implies & 0xFF
+ *(bytes++) = (byte)(iTemp >> 16); // Implies & 0xFF, which isn't needed cause high are all 0
+ *(bytes++) = (byte)(0x00);
+ }
+ continue;
+ }
+
+ // We are missing our low surrogate, decrement chars and fallback the high surrogate
+ // The high surrogate may have come from the encoder, but nothing else did.
+ Debug.Assert(chars > charStart,
+ "[UTF32Encoding.GetBytes]Expected chars to have advanced if no low surrogate");
+ chars--;
+
+ // Do the fallback
+ charsForFallback = chars;
+ fallbackBuffer.InternalFallback(highSurrogate, ref charsForFallback);
+ chars = charsForFallback;
+
+ // We're going to fallback the old high surrogate.
+ highSurrogate = '\0';
+ continue;
+ }
+
+ // Do we have another high surrogate?, if so remember it
+ if (Char.IsHighSurrogate(ch))
+ {
+ //
+ // We'll have a high surrogate to check next time.
+ //
+ highSurrogate = ch;
+ continue;
+ }
+
+ // Check for illegal characters (low surrogate)
+ if (Char.IsLowSurrogate(ch))
+ {
+ // We have a leading low surrogate, do the fallback
+ charsForFallback = chars;
+ fallbackBuffer.InternalFallback(ch, ref charsForFallback);
+ chars = charsForFallback;
+
+ // Try again with fallback buffer
+ continue;
+ }
+
+ // We get to add the character, yippee.
+ if (bytes + 3 >= byteEnd)
+ {
+ // Don't have 4 bytes
+ if (fallbackBuffer.bFallingBack)
+ fallbackBuffer.MovePrevious(); // Aren't using this fallback char
+ else
+ {
+ // Must've advanced already
+ Debug.Assert(chars > charStart,
+ "[UTF32Encoding.GetBytes]Expected chars to have advanced if normal character");
+ chars--; // Aren't using this char
+ }
+ ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written)
+ break; // Didn't throw, stop
+ }
+
+ if (_bigEndian)
+ {
+ *(bytes++) = (byte)(0x00);
+ *(bytes++) = (byte)(0x00);
+ *(bytes++) = (byte)((uint)ch >> 8); // Implies & 0xFF
+ *(bytes++) = (byte)(ch); // Implies & 0xFF
+ }
+ else
+ {
+ *(bytes++) = (byte)(ch); // Implies & 0xFF
+ *(bytes++) = (byte)((uint)ch >> 8); // Implies & 0xFF
+ *(bytes++) = (byte)(0x00);
+ *(bytes++) = (byte)(0x00);
+ }
+ }
+
+ // May have to do our last surrogate
+ if ((encoder == null || encoder.MustFlush) && highSurrogate > 0)
+ {
+ // We have to do the fallback for the lonely high surrogate
+ charsForFallback = chars;
+ fallbackBuffer.InternalFallback(highSurrogate, ref charsForFallback);
+ chars = charsForFallback;
+
+ highSurrogate = (char)0;
+ goto TryAgain;
+ }
+
+ // Fix our encoder if we have one
+ Debug.Assert(highSurrogate == 0 || (encoder != null && !encoder.MustFlush),
+ "[UTF32Encoding.GetBytes]Expected encoder to be flushed.");
+
+ if (encoder != null)
+ {
+ // Remember our left over surrogate (or 0 if flushing)
+ encoder.charLeftOver = highSurrogate;
+
+ // Need # chars used
+ encoder.m_charsUsed = (int)(chars - charStart);
+ }
+
+ // return the new length
+ return (int)(bytes - byteStart);
+ }
+
+ internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
+ {
+ Debug.Assert(bytes != null, "[UTF32Encoding.GetCharCount]bytes!=null");
+ Debug.Assert(count >= 0, "[UTF32Encoding.GetCharCount]count >=0");
+
+ UTF32Decoder decoder = (UTF32Decoder)baseDecoder;
+
+ // None so far!
+ int charCount = 0;
+ byte* end = bytes + count;
+ byte* byteStart = bytes;
+
+ // Set up decoder
+ int readCount = 0;
+ uint iChar = 0;
+
+ // For fallback we may need a fallback buffer
+ DecoderFallbackBuffer fallbackBuffer = null;
+
+ // See if there's anything in our decoder
+ if (decoder != null)
+ {
+ readCount = decoder.readByteCount;
+ iChar = (uint)decoder.iChar;
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Shouldn't have anything in fallback buffer for GetCharCount
+ // (don't have to check m_throwOnOverflow for chars or count)
+ Debug.Assert(fallbackBuffer.Remaining == 0,
+ "[UTF32Encoding.GetCharCount]Expected empty fallback buffer at start");
+ }
+ else
+ {
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ }
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, null);
+
+ // Loop through our input, 4 characters at a time!
+ while (bytes < end && charCount >= 0)
+ {
+ // Get our next character
+ if (_bigEndian)
+ {
+ // Scoot left and add it to the bottom
+ iChar <<= 8;
+ iChar += *(bytes++);
+ }
+ else
+ {
+ // Scoot right and add it to the top
+ iChar >>= 8;
+ iChar += (uint)(*(bytes++)) << 24;
+ }
+
+ readCount++;
+
+ // See if we have all the bytes yet
+ if (readCount < 4)
+ continue;
+
+ // Have the bytes
+ readCount = 0;
+
+ // See if its valid to encode
+ if (iChar > 0x10FFFF || (iChar >= 0xD800 && iChar <= 0xDFFF))
+ {
+ // Need to fall back these 4 bytes
+ byte[] fallbackBytes;
+ if (_bigEndian)
+ {
+ fallbackBytes = new byte[] {
+ unchecked((byte)(iChar>>24)), unchecked((byte)(iChar>>16)),
+ unchecked((byte)(iChar>>8)), unchecked((byte)(iChar)) };
+ }
+ else
+ {
+ fallbackBytes = new byte[] {
+ unchecked((byte)(iChar)), unchecked((byte)(iChar>>8)),
+ unchecked((byte)(iChar>>16)), unchecked((byte)(iChar>>24)) };
+ }
+
+ charCount += fallbackBuffer.InternalFallback(fallbackBytes, bytes);
+
+ // Ignore the illegal character
+ iChar = 0;
+ continue;
+ }
+
+ // Ok, we have something we can add to our output
+ if (iChar >= 0x10000)
+ {
+ // Surrogates take 2
+ charCount++;
+ }
+
+ // Add the rest of the surrogate or our normal character
+ charCount++;
+
+ // iChar is back to 0
+ iChar = 0;
+ }
+
+ // See if we have something left over that has to be decoded
+ if (readCount > 0 && (decoder == null || decoder.MustFlush))
+ {
+ // Oops, there's something left over with no place to go.
+ byte[] fallbackBytes = new byte[readCount];
+ if (_bigEndian)
+ {
+ while (readCount > 0)
+ {
+ fallbackBytes[--readCount] = unchecked((byte)iChar);
+ iChar >>= 8;
+ }
+ }
+ else
+ {
+ while (readCount > 0)
+ {
+ fallbackBytes[--readCount] = unchecked((byte)(iChar >> 24));
+ iChar <<= 8;
+ }
+ }
+
+ charCount += fallbackBuffer.InternalFallback(fallbackBytes, bytes);
+ }
+
+ // Check for overflows.
+ if (charCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow);
+
+ // Shouldn't have anything in fallback buffer for GetCharCount
+ // (don't have to check m_throwOnOverflow for chars or count)
+ Debug.Assert(fallbackBuffer.Remaining == 0,
+ "[UTF32Encoding.GetCharCount]Expected empty fallback buffer at end");
+
+ // Return our count
+ return charCount;
+ }
+
+ internal override unsafe int GetChars(byte* bytes, int byteCount,
+ char* chars, int charCount, DecoderNLS baseDecoder)
+ {
+ Debug.Assert(chars != null, "[UTF32Encoding.GetChars]chars!=null");
+ Debug.Assert(bytes != null, "[UTF32Encoding.GetChars]bytes!=null");
+ Debug.Assert(byteCount >= 0, "[UTF32Encoding.GetChars]byteCount >=0");
+ Debug.Assert(charCount >= 0, "[UTF32Encoding.GetChars]charCount >=0");
+
+ UTF32Decoder decoder = (UTF32Decoder)baseDecoder;
+
+ // None so far!
+ char* charStart = chars;
+ char* charEnd = chars + charCount;
+
+ byte* byteStart = bytes;
+ byte* byteEnd = bytes + byteCount;
+
+ // See if there's anything in our decoder (but don't clear it yet)
+ int readCount = 0;
+ uint iChar = 0;
+
+ // For fallback we may need a fallback buffer
+ DecoderFallbackBuffer fallbackBuffer = null;
+ char* charsForFallback;
+
+ // See if there's anything in our decoder
+ if (decoder != null)
+ {
+ readCount = decoder.readByteCount;
+ iChar = (uint)decoder.iChar;
+ fallbackBuffer = baseDecoder.FallbackBuffer;
+
+ // Shouldn't have anything in fallback buffer for GetChars
+ // (don't have to check m_throwOnOverflow for chars)
+ Debug.Assert(fallbackBuffer.Remaining == 0,
+ "[UTF32Encoding.GetChars]Expected empty fallback buffer at start");
+ }
+ else
+ {
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ }
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(bytes, chars + charCount);
+
+ // Loop through our input, 4 characters at a time!
+ while (bytes < byteEnd)
+ {
+ // Get our next character
+ if (_bigEndian)
+ {
+ // Scoot left and add it to the bottom
+ iChar <<= 8;
+ iChar += *(bytes++);
+ }
+ else
+ {
+ // Scoot right and add it to the top
+ iChar >>= 8;
+ iChar += (uint)(*(bytes++)) << 24;
+ }
+
+ readCount++;
+
+ // See if we have all the bytes yet
+ if (readCount < 4)
+ continue;
+
+ // Have the bytes
+ readCount = 0;
+
+ // See if its valid to encode
+ if (iChar > 0x10FFFF || (iChar >= 0xD800 && iChar <= 0xDFFF))
+ {
+ // Need to fall back these 4 bytes
+ byte[] fallbackBytes;
+ if (_bigEndian)
+ {
+ fallbackBytes = new byte[] {
+ unchecked((byte)(iChar>>24)), unchecked((byte)(iChar>>16)),
+ unchecked((byte)(iChar>>8)), unchecked((byte)(iChar)) };
+ }
+ else
+ {
+ fallbackBytes = new byte[] {
+ unchecked((byte)(iChar)), unchecked((byte)(iChar>>8)),
+ unchecked((byte)(iChar>>16)), unchecked((byte)(iChar>>24)) };
+ }
+
+ // Chars won't be updated unless this works.
+ charsForFallback = chars;
+ bool fallbackResult = fallbackBuffer.InternalFallback(fallbackBytes, bytes, ref charsForFallback);
+ chars = charsForFallback;
+
+ if (!fallbackResult)
+ {
+
+ // Couldn't fallback, throw or wait til next time
+ // We either read enough bytes for bytes-=4 to work, or we're
+ // going to throw in ThrowCharsOverflow because chars == charStart
+ Debug.Assert(bytes >= byteStart + 4 || chars == charStart,
+ "[UTF32Encoding.GetChars]Expected to have consumed bytes or throw (bad surrogate)");
+ bytes -= 4; // get back to where we were
+ iChar = 0; // Remembering nothing
+ fallbackBuffer.InternalReset();
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ break; // Stop here, didn't throw
+ }
+
+ // Ignore the illegal character
+ iChar = 0;
+ continue;
+ }
+
+
+ // Ok, we have something we can add to our output
+ if (iChar >= 0x10000)
+ {
+ // Surrogates take 2
+ if (chars >= charEnd - 1)
+ {
+ // Throwing or stopping
+ // We either read enough bytes for bytes-=4 to work, or we're
+ // going to throw in ThrowCharsOverflow because chars == charStart
+ Debug.Assert(bytes >= byteStart + 4 || chars == charStart,
+ "[UTF32Encoding.GetChars]Expected to have consumed bytes or throw (surrogate)");
+ bytes -= 4; // get back to where we were
+ iChar = 0; // Remembering nothing
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ break; // Stop here, didn't throw
+ }
+
+ *(chars++) = GetHighSurrogate(iChar);
+ iChar = GetLowSurrogate(iChar);
+ }
+ // Bounds check for normal character
+ else if (chars >= charEnd)
+ {
+ // Throwing or stopping
+ // We either read enough bytes for bytes-=4 to work, or we're
+ // going to throw in ThrowCharsOverflow because chars == charStart
+ Debug.Assert(bytes >= byteStart + 4 || chars == charStart,
+ "[UTF32Encoding.GetChars]Expected to have consumed bytes or throw (normal char)");
+ bytes -= 4; // get back to where we were
+ iChar = 0; // Remembering nothing
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ break; // Stop here, didn't throw
+ }
+
+ // Add the rest of the surrogate or our normal character
+ *(chars++) = (char)iChar;
+
+ // iChar is back to 0
+ iChar = 0;
+ }
+
+ // See if we have something left over that has to be decoded
+ if (readCount > 0 && (decoder == null || decoder.MustFlush))
+ {
+ // Oops, there's something left over with no place to go.
+ byte[] fallbackBytes = new byte[readCount];
+ int tempCount = readCount;
+ if (_bigEndian)
+ {
+ while (tempCount > 0)
+ {
+ fallbackBytes[--tempCount] = unchecked((byte)iChar);
+ iChar >>= 8;
+ }
+ }
+ else
+ {
+ while (tempCount > 0)
+ {
+ fallbackBytes[--tempCount] = unchecked((byte)(iChar >> 24));
+ iChar <<= 8;
+ }
+ }
+
+ charsForFallback = chars;
+ bool fallbackResult = fallbackBuffer.InternalFallback(fallbackBytes, bytes, ref charsForFallback);
+ chars = charsForFallback;
+
+ if (!fallbackResult)
+ {
+ // Couldn't fallback.
+ fallbackBuffer.InternalReset();
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ // Stop here, didn't throw, backed up, so still nothing in buffer
+ }
+ else
+ {
+ // Don't clear our decoder unless we could fall it back.
+ // If we caught the if above, then we're a convert() and will catch this next time.
+ readCount = 0;
+ iChar = 0;
+ }
+ }
+
+ // Remember any left over stuff, clearing buffer as well for MustFlush
+ if (decoder != null)
+ {
+ decoder.iChar = (int)iChar;
+ decoder.readByteCount = readCount;
+ decoder.m_bytesUsed = (int)(bytes - byteStart);
+ }
+
+ // Shouldn't have anything in fallback buffer for GetChars
+ // (don't have to check m_throwOnOverflow for chars)
+ Debug.Assert(fallbackBuffer.Remaining == 0,
+ "[UTF32Encoding.GetChars]Expected empty fallback buffer at end");
+
+ // Return our count
+ return (int)(chars - charStart);
+ }
+
+
+ private uint GetSurrogate(char cHigh, char cLow)
+ {
+ return (((uint)cHigh - 0xD800) * 0x400) + ((uint)cLow - 0xDC00) + 0x10000;
+ }
+
+ private char GetHighSurrogate(uint iChar)
+ {
+ return (char)((iChar - 0x10000) / 0x400 + 0xD800);
+ }
+
+ private char GetLowSurrogate(uint iChar)
+ {
+ return (char)((iChar - 0x10000) % 0x400 + 0xDC00);
+ }
+
+
+ public override Decoder GetDecoder()
+ {
+ return new UTF32Decoder(this);
+ }
+
+
+ public override Encoder GetEncoder()
+ {
+ return new EncoderNLS(this);
+ }
+
+
+ public override int GetMaxByteCount(int charCount)
+ {
+ if (charCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(charCount),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback
+ long byteCount = (long)charCount + 1;
+
+ if (EncoderFallback.MaxCharCount > 1)
+ byteCount *= EncoderFallback.MaxCharCount;
+
+ // 4 bytes per char
+ byteCount *= 4;
+
+ if (byteCount > 0x7fffffff)
+ throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
+
+ return (int)byteCount;
+ }
+
+
+ public override int GetMaxCharCount(int byteCount)
+ {
+ if (byteCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(byteCount),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // A supplementary character becomes 2 surrogate characters, so 4 input bytes becomes 2 chars,
+ // plus we may have 1 surrogate char left over if the decoder has 3 bytes in it already for a non-bmp char.
+ // Have to add another one because 1/2 == 0, but 3 bytes left over could be 2 char surrogate pair
+ int charCount = (byteCount / 2) + 2;
+
+ // Also consider fallback because our input bytes could be out of range of unicode.
+ // Since fallback would fallback 4 bytes at a time, we'll only fall back 1/2 of MaxCharCount.
+ if (DecoderFallback.MaxCharCount > 2)
+ {
+ // Multiply time fallback size
+ charCount *= DecoderFallback.MaxCharCount;
+
+ // We were already figuring 2 chars per 4 bytes, but fallback will be different #
+ charCount /= 2;
+ }
+
+ if (charCount > 0x7fffffff)
+ throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow);
+
+ return (int)charCount;
+ }
+
+
+ public override byte[] GetPreamble()
+ {
+ if (_emitUTF32ByteOrderMark)
+ {
+ // Allocate new array to prevent users from modifying it.
+ if (_bigEndian)
+ {
+ return new byte[4] { 0x00, 0x00, 0xFE, 0xFF };
+ }
+ else
+ {
+ return new byte[4] { 0xFF, 0xFE, 0x00, 0x00 }; // 00 00 FE FF
+ }
+ }
+ else
+ return Array.Empty<byte>();
+ }
+
+
+ public override bool Equals(Object value)
+ {
+ UTF32Encoding that = value as UTF32Encoding;
+ if (that != null)
+ {
+ return (_emitUTF32ByteOrderMark == that._emitUTF32ByteOrderMark) &&
+ (_bigEndian == that._bigEndian) &&
+ (EncoderFallback.Equals(that.EncoderFallback)) &&
+ (DecoderFallback.Equals(that.DecoderFallback));
+ }
+ return (false);
+ }
+
+
+ public override int GetHashCode()
+ {
+ //Not great distribution, but this is relatively unlikely to be used as the key in a hashtable.
+ return this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode() +
+ CodePage + (_emitUTF32ByteOrderMark ? 4 : 0) + (_bigEndian ? 8 : 0);
+ }
+
+ [Serializable]
+ private sealed class UTF32Decoder : DecoderNLS
+ {
+ // Need a place to store any extra bytes we may have picked up
+ internal int iChar = 0;
+ internal int readByteCount = 0;
+
+ public UTF32Decoder(UTF32Encoding encoding) : base(encoding)
+ {
+ // base calls reset
+ }
+
+ public override void Reset()
+ {
+ this.iChar = 0;
+ this.readByteCount = 0;
+ if (m_fallbackBuffer != null)
+ m_fallbackBuffer.Reset();
+ }
+
+ // Anything left in our decoder?
+ internal override bool HasState
+ {
+ get
+ {
+ // ReadByteCount is our flag. (iChar==0 doesn't mean much).
+ return (this.readByteCount != 0);
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/UTF8Encoding.cs b/src/mscorlib/shared/System/Text/UTF8Encoding.cs
new file mode 100644
index 0000000000..5cfa89018a
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/UTF8Encoding.cs
@@ -0,0 +1,2668 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// The worker functions in this file was optimized for performance. If you make changes
+// you should use care to consider all of the interesting cases.
+
+// The code of all worker functions in this file is written twice: Once as as a slow loop, and the
+// second time as a fast loop. The slow loops handles all special cases, throws exceptions, etc.
+// The fast loops attempts to blaze through as fast as possible with optimistic range checks,
+// processing multiple characters at a time, and falling back to the slow loop for all special cases.
+
+// This define can be used to turn off the fast loops. Useful for finding whether
+// the problem is fastloop-specific.
+#define FASTLOOP
+
+using System;
+using System.Runtime.Serialization;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Globalization;
+
+namespace System.Text
+{
+ // Encodes text into and out of UTF-8. UTF-8 is a way of writing
+ // Unicode characters with variable numbers of bytes per character,
+ // optimized for the lower 127 ASCII characters. It's an efficient way
+ // of encoding US English in an internationalizable way.
+ //
+ // Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused.
+ //
+ // The UTF-8 byte order mark is simply the Unicode byte order mark
+ // (0xFEFF) written in UTF-8 (0xEF 0xBB 0xBF). The byte order mark is
+ // used mostly to distinguish UTF-8 text from other encodings, and doesn't
+ // switch the byte orderings.
+
+ [Serializable]
+ public class UTF8Encoding : Encoding
+ {
+ /*
+ bytes bits UTF-8 representation
+ ----- ---- -----------------------------------
+ 1 7 0vvvvvvv
+ 2 11 110vvvvv 10vvvvvv
+ 3 16 1110vvvv 10vvvvvv 10vvvvvv
+ 4 21 11110vvv 10vvvvvv 10vvvvvv 10vvvvvv
+ ----- ---- -----------------------------------
+
+ Surrogate:
+ Real Unicode value = (HighSurrogate - 0xD800) * 0x400 + (LowSurrogate - 0xDC00) + 0x10000
+ */
+
+ private const int UTF8_CODEPAGE = 65001;
+
+ // Allow for devirtualization (see https://github.com/dotnet/coreclr/pull/9230)
+ [Serializable]
+ internal sealed class UTF8EncodingSealed : UTF8Encoding
+ {
+ public UTF8EncodingSealed(bool encoderShouldEmitUTF8Identifier) : base(encoderShouldEmitUTF8Identifier) { }
+ }
+
+ // Used by Encoding.UTF8 for lazy initialization
+ // The initialization code will not be run until a static member of the class is referenced
+ internal static readonly UTF8EncodingSealed s_default = new UTF8EncodingSealed(encoderShouldEmitUTF8Identifier: true);
+
+ // Yes, the idea of emitting U+FEFF as a UTF-8 identifier has made it into
+ // the standard.
+ private bool _emitUTF8Identifier = false;
+
+ private bool _isThrowException = false;
+
+
+ public UTF8Encoding() : this(false)
+ {
+ }
+
+
+ public UTF8Encoding(bool encoderShouldEmitUTF8Identifier) :
+ this(encoderShouldEmitUTF8Identifier, false)
+ {
+ }
+
+
+ public UTF8Encoding(bool encoderShouldEmitUTF8Identifier, bool throwOnInvalidBytes) :
+ base(UTF8_CODEPAGE)
+ {
+ _emitUTF8Identifier = encoderShouldEmitUTF8Identifier;
+ _isThrowException = throwOnInvalidBytes;
+
+ // Encoding's constructor already did this, but it'll be wrong if we're throwing exceptions
+ if (_isThrowException)
+ SetDefaultFallbacks();
+ }
+
+ internal override void SetDefaultFallbacks()
+ {
+ // For UTF-X encodings, we use a replacement fallback with an empty string
+ if (_isThrowException)
+ {
+ this.encoderFallback = EncoderFallback.ExceptionFallback;
+ this.decoderFallback = DecoderFallback.ExceptionFallback;
+ }
+ else
+ {
+ this.encoderFallback = new EncoderReplacementFallback("\xFFFD");
+ this.decoderFallback = new DecoderReplacementFallback("\xFFFD");
+ }
+ }
+
+
+ // WARNING: GetByteCount(string chars)
+ // WARNING: has different variable names than EncodingNLS.cs, so this can't just be cut & pasted,
+ // WARNING: otherwise it'll break VB's way of declaring these.
+ //
+ // The following methods are copied from EncodingNLS.cs.
+ // Unfortunately EncodingNLS.cs is internal and we're public, so we have to reimpliment them here.
+ // These should be kept in sync for the following classes:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ // Returns the number of bytes required to encode a range of characters in
+ // a character array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetByteCount(char[] chars, int index, int count)
+ {
+ // Validate input parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - index < count)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input, return 0, avoid fixed empty array problem
+ if (count == 0)
+ return 0;
+
+ // Just call the pointer version
+ fixed (char* pChars = chars)
+ return GetByteCount(pChars + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetByteCount(String chars)
+ {
+ // Validate input
+ if (chars==null)
+ throw new ArgumentNullException("s");
+ Contract.EndContractBlock();
+
+ fixed (char* pChars = chars)
+ return GetByteCount(pChars, chars.Length, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetByteCount(char* chars, int count)
+ {
+ // Validate Parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Call it with empty encoder
+ return GetByteCount(chars, count, null);
+ }
+
+ // Parent method is safe.
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ public override unsafe int GetBytes(String s, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ if (s == null || bytes == null)
+ throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (s.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like 0 length arrays.
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0])
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // Encodes a range of characters in a character array into a range of bytes
+ // in a byte array. An exception occurs if the byte array is not large
+ // enough to hold the complete encoding of the characters. The
+ // GetByteCount method can be used to determine the exact number of
+ // bytes that will be produced for a given range of characters.
+ // Alternatively, the GetMaxByteCount method can be used to
+ // determine the maximum number of bytes that will be produced for a given
+ // number of characters, regardless of the actual character values.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ // Validate parameters
+ if (chars == null || bytes == null)
+ throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If nothing to encode return 0, avoid fixed problem
+ if (charCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like 0 length arrays.
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0])
+ // Remember that byteCount is # to decode, not size of array.
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetBytes(chars, charCount, bytes, byteCount, null);
+ }
+
+ // Returns the number of characters produced by decoding a range of bytes
+ // in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetCharCount(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - index < count)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input just return 0, fixed doesn't like 0 length arrays.
+ if (count == 0)
+ return 0;
+
+ // Just call pointer version
+ fixed (byte* pBytes = bytes)
+ return GetCharCount(pBytes + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetCharCount(byte* bytes, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetCharCount(bytes, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
+ char[] chars, int charIndex)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (byteIndex < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if ( bytes.Length - byteIndex < byteCount)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (charIndex < 0 || charIndex > chars.Length)
+ throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If no input, return 0 & avoid fixed problem
+ if (byteCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int charCount = chars.Length - charIndex;
+
+ // Fixed doesn't like 0 length arrays.
+ if (chars.Length == 0)
+ chars = new char[1];
+
+ fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0])
+ // Remember that charCount is # to decode, not size of array
+ return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetChars(bytes, byteCount, chars, charCount, null);
+ }
+
+ // Returns a string containing the decoded representation of a range of
+ // bytes in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe String GetString(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - index < count)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // Avoid problems with empty input buffer
+ if (count == 0) return String.Empty;
+
+ fixed (byte* pBytes = bytes)
+ return String.CreateStringFromEncoding(
+ pBytes + index, count, this);
+ }
+
+ //
+ // End of standard methods copied from EncodingNLS.cs
+ //
+
+ // To simplify maintenance, the structure of GetByteCount and GetBytes should be
+ // kept the same as much as possible
+ internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder)
+ {
+ // For fallback we may need a fallback buffer.
+ // We wait to initialize it though in case we don't have any broken input unicode
+ EncoderFallbackBuffer fallbackBuffer = null;
+ char* pSrcForFallback;
+
+ char* pSrc = chars;
+ char* pEnd = pSrc + count;
+
+ // Start by assuming we have as many as count
+ int byteCount = count;
+
+ int ch = 0;
+
+ if (baseEncoder != null)
+ {
+ UTF8Encoder encoder = (UTF8Encoder)baseEncoder;
+ ch = encoder.surrogateChar;
+
+ // We mustn't have left over fallback data when counting
+ if (encoder.InternalHasFallbackBuffer)
+ {
+ fallbackBuffer = encoder.FallbackBuffer;
+ if (fallbackBuffer.Remaining > 0)
+ throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(chars, pEnd, encoder, false);
+ }
+ }
+
+ for (;;)
+ {
+ // SLOWLOOP: does all range checks, handles all special cases, but it is slow
+ if (pSrc >= pEnd)
+ {
+ if (ch == 0)
+ {
+ // Unroll any fallback that happens at the end
+ ch = fallbackBuffer != null ? fallbackBuffer.InternalGetNextChar() : 0;
+ if (ch > 0)
+ {
+ byteCount++;
+ goto ProcessChar;
+ }
+ }
+ else
+ {
+ // Case of surrogates in the fallback.
+ if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
+ {
+ Debug.Assert(ch >= 0xD800 && ch <= 0xDBFF,
+ "[UTF8Encoding.GetBytes]expected high surrogate, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
+
+ ch = fallbackBuffer.InternalGetNextChar();
+ byteCount++;
+
+ if (InRange(ch, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ ch = 0xfffd;
+ byteCount++;
+ goto EncodeChar;
+ }
+ else if (ch > 0)
+ {
+ goto ProcessChar;
+ }
+ else
+ {
+ byteCount--; // ignore last one.
+ break;
+ }
+ }
+ }
+
+ if (ch <= 0)
+ {
+ break;
+ }
+ if (baseEncoder != null && !baseEncoder.MustFlush)
+ {
+ break;
+ }
+
+ // attempt to encode the partial surrogate (will fallback or ignore it), it'll also subtract 1.
+ byteCount++;
+ goto EncodeChar;
+ }
+
+ if (ch > 0)
+ {
+ Debug.Assert(ch >= 0xD800 && ch <= 0xDBFF,
+ "[UTF8Encoding.GetBytes]expected high surrogate, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
+
+ // use separate helper variables for local contexts so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ int cha = *pSrc;
+
+ // count the pending surrogate
+ byteCount++;
+
+ // In previous byte, we encountered a high surrogate, so we are expecting a low surrogate here.
+ // if (IsLowSurrogate(cha)) {
+ if (InRange(cha, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ // Don't need a real # because we're just counting, anything > 0x7ff ('cept surrogate) will do.
+ ch = 0xfffd;
+ // ch = cha + (ch << 10) +
+ // (0x10000
+ // - CharUnicodeInfo.LOW_SURROGATE_START
+ // - (CharUnicodeInfo.HIGH_SURROGATE_START << 10) );
+
+ // Use this next char
+ pSrc++;
+ }
+ // else ch is still high surrogate and encoding will fail (so don't add count)
+
+ // attempt to encode the surrogate or partial surrogate
+ goto EncodeChar;
+ }
+
+ // If we've used a fallback, then we have to check for it
+ if (fallbackBuffer != null)
+ {
+ ch = fallbackBuffer.InternalGetNextChar();
+ if (ch > 0)
+ {
+ // We have an extra byte we weren't expecting.
+ byteCount++;
+ goto ProcessChar;
+ }
+ }
+
+ // read next char. The JIT optimization seems to be getting confused when
+ // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
+ ch = *pSrc;
+ pSrc++;
+
+ ProcessChar:
+ // if (IsHighSurrogate(ch)) {
+ if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.HIGH_SURROGATE_END))
+ {
+ // we will count this surrogate next time around
+ byteCount--;
+ continue;
+ }
+ // either good char or partial surrogate
+
+ EncodeChar:
+ // throw exception on partial surrogate if necessary
+ // if (IsLowSurrogate(ch) || IsHighSurrogate(ch))
+ if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ // Lone surrogates aren't allowed
+ // Have to make a fallback buffer if we don't have one
+ if (fallbackBuffer == null)
+ {
+ // wait on fallbacks if we can
+ // For fallback we may need a fallback buffer
+ if (baseEncoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = baseEncoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(chars, chars + count, baseEncoder, false);
+ }
+
+ // Do our fallback. Actually we already know its a mixed up surrogate,
+ // so the ref pSrc isn't gonna do anything.
+ pSrcForFallback = pSrc; // Avoid passing pSrc by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(unchecked((char)ch), ref pSrcForFallback);
+ pSrc = pSrcForFallback;
+
+ // Ignore it if we don't throw (we had preallocated this ch)
+ byteCount--;
+ ch = 0;
+ continue;
+ }
+
+ // Count them
+ if (ch > 0x7F)
+ {
+ if (ch > 0x7FF)
+ {
+ // the extra surrogate byte was compensated by the second surrogate character
+ // (2 surrogates make 4 bytes. We've already counted 2 bytes, 1 per char)
+ byteCount++;
+ }
+ byteCount++;
+ }
+
+#if BIT64
+ // check for overflow
+ if (byteCount < 0)
+ {
+ break;
+ }
+#endif
+
+#if FASTLOOP
+ // If still have fallback don't do fast loop
+ if (fallbackBuffer != null && (ch = fallbackBuffer.InternalGetNextChar()) != 0)
+ {
+ // We're reserving 1 byte for each char by default
+ byteCount++;
+ goto ProcessChar;
+ }
+
+ int availableChars = PtrDiff(pEnd, pSrc);
+
+ // don't fall into the fast decoding loop if we don't have enough characters
+ if (availableChars <= 13)
+ {
+ // try to get over the remainder of the ascii characters fast though
+ char* pLocalEnd = pEnd; // hint to get pLocalEnd enregistered
+ while (pSrc < pLocalEnd)
+ {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F)
+ goto ProcessChar;
+ }
+
+ // we are done
+ break;
+ }
+
+#if BIT64
+ // make sure that we won't get a silent overflow inside the fast loop
+ // (Fall out to slow loop if we have this many characters)
+ availableChars &= 0x0FFFFFFF;
+#endif
+
+ // To compute the upper bound, assume that all characters are ASCII characters at this point,
+ // the boundary will be decreased for every non-ASCII character we encounter
+ // Also, we need 3 + 4 chars reserve for the unrolled ansi decoding loop and for decoding of surrogates
+ char* pStop = pSrc + availableChars - (3 + 4);
+
+ while (pSrc < pStop)
+ {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F) // Not ASCII
+ {
+ if (ch > 0x7FF) // Not 2 Byte
+ {
+ if ((ch & 0xF800) == 0xD800) // See if its a Surrogate
+ goto LongCode;
+ byteCount++;
+ }
+ byteCount++;
+ }
+
+ // get pSrc aligned
+ if ((unchecked((int)pSrc) & 0x2) != 0)
+ {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F) // Not ASCII
+ {
+ if (ch > 0x7FF) // Not 2 Byte
+ {
+ if ((ch & 0xF800) == 0xD800) // See if its a Surrogate
+ goto LongCode;
+ byteCount++;
+ }
+ byteCount++;
+ }
+ }
+
+ // Run 2 * 4 characters at a time!
+ while (pSrc < pStop)
+ {
+ ch = *(int*)pSrc;
+ int chc = *(int*)(pSrc + 2);
+ if (((ch | chc) & unchecked((int)0xFF80FF80)) != 0) // See if not ASCII
+ {
+ if (((ch | chc) & unchecked((int)0xF800F800)) != 0) // See if not 2 Byte
+ {
+ goto LongCodeWithMask;
+ }
+
+
+ if ((ch & unchecked((int)0xFF800000)) != 0) // Actually 0x07800780 is all we care about (4 bits)
+ byteCount++;
+ if ((ch & unchecked((int)0xFF80)) != 0)
+ byteCount++;
+ if ((chc & unchecked((int)0xFF800000)) != 0)
+ byteCount++;
+ if ((chc & unchecked((int)0xFF80)) != 0)
+ byteCount++;
+ }
+ pSrc += 4;
+
+ ch = *(int*)pSrc;
+ chc = *(int*)(pSrc + 2);
+ if (((ch | chc) & unchecked((int)0xFF80FF80)) != 0) // See if not ASCII
+ {
+ if (((ch | chc) & unchecked((int)0xF800F800)) != 0) // See if not 2 Byte
+ {
+ goto LongCodeWithMask;
+ }
+
+ if ((ch & unchecked((int)0xFF800000)) != 0)
+ byteCount++;
+ if ((ch & unchecked((int)0xFF80)) != 0)
+ byteCount++;
+ if ((chc & unchecked((int)0xFF800000)) != 0)
+ byteCount++;
+ if ((chc & unchecked((int)0xFF80)) != 0)
+ byteCount++;
+ }
+ pSrc += 4;
+ }
+ break;
+
+ LongCodeWithMask:
+#if BIGENDIAN
+ // be careful about the sign extension
+ ch = (int)(((uint)ch) >> 16);
+#else // BIGENDIAN
+ ch = (char)ch;
+#endif // BIGENDIAN
+ pSrc++;
+
+ if (ch <= 0x7F)
+ {
+ continue;
+ }
+
+ LongCode:
+ // use separate helper variables for slow and fast loop so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ if (ch > 0x7FF)
+ {
+ // if (IsLowSurrogate(ch) || IsHighSurrogate(ch))
+ if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ // 4 byte encoding - high surrogate + low surrogate
+
+ int chd = *pSrc;
+ if (
+ // !IsHighSurrogate(ch) // low without high -> bad
+ ch > CharUnicodeInfo.HIGH_SURROGATE_END ||
+ // !IsLowSurrogate(chd) // high not followed by low -> bad
+ !InRange(chd, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ // Back up and drop out to slow loop to figure out error
+ pSrc--;
+ break;
+ }
+ pSrc++;
+
+ // byteCount - this byte is compensated by the second surrogate character
+ }
+ byteCount++;
+ }
+ byteCount++;
+
+ // byteCount - the last byte is already included
+ }
+#endif // FASTLOOP
+
+ // no pending char at this point
+ ch = 0;
+ }
+
+#if BIT64
+ // check for overflow
+ if (byteCount < 0)
+ {
+ throw new ArgumentException(
+ SR.Argument_ConversionOverflow);
+ }
+#endif
+
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
+ "[UTF8Encoding.GetByteCount]Expected Empty fallback buffer");
+
+ return byteCount;
+ }
+
+ // diffs two char pointers using unsigned arithmetic. The unsigned arithmetic
+ // is good enough for us, and it tends to generate better code than the signed
+ // arithmetic generated by default
+ unsafe private static int PtrDiff(char* a, char* b)
+ {
+ return (int)(((uint)((byte*)a - (byte*)b)) >> 1);
+ }
+
+ // byte* flavor just for parity
+ unsafe private static int PtrDiff(byte* a, byte* b)
+ {
+ return (int)(a - b);
+ }
+
+ private static bool InRange(int ch, int start, int end)
+ {
+ return (uint)(ch - start) <= (uint)(end - start);
+ }
+
+ // Our workhorse
+ // Note: We ignore mismatched surrogates, unless the exception flag is set in which case we throw
+ internal override unsafe int GetBytes(char* chars, int charCount,
+ byte* bytes, int byteCount, EncoderNLS baseEncoder)
+ {
+ Debug.Assert(chars != null, "[UTF8Encoding.GetBytes]chars!=null");
+ Debug.Assert(byteCount >= 0, "[UTF8Encoding.GetBytes]byteCount >=0");
+ Debug.Assert(charCount >= 0, "[UTF8Encoding.GetBytes]charCount >=0");
+ Debug.Assert(bytes != null, "[UTF8Encoding.GetBytes]bytes!=null");
+
+ UTF8Encoder encoder = null;
+
+ // For fallback we may need a fallback buffer.
+ // We wait to initialize it though in case we don't have any broken input unicode
+ EncoderFallbackBuffer fallbackBuffer = null;
+ char* pSrcForFallback;
+
+ char* pSrc = chars;
+ byte* pTarget = bytes;
+
+ char* pEnd = pSrc + charCount;
+ byte* pAllocatedBufferEnd = pTarget + byteCount;
+
+ int ch = 0;
+
+ // assume that JIT will enregister pSrc, pTarget and ch
+
+ if (baseEncoder != null)
+ {
+ encoder = (UTF8Encoder)baseEncoder;
+ ch = encoder.surrogateChar;
+
+ // We mustn't have left over fallback data when counting
+ if (encoder.InternalHasFallbackBuffer)
+ {
+ // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary
+ fallbackBuffer = encoder.FallbackBuffer;
+ if (fallbackBuffer.Remaining > 0 && encoder.m_throwOnOverflow)
+ throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(chars, pEnd, encoder, true);
+ }
+ }
+
+ for (;;)
+ {
+ // SLOWLOOP: does all range checks, handles all special cases, but it is slow
+
+ if (pSrc >= pEnd)
+ {
+ if (ch == 0)
+ {
+ // Check if there's anthing left to get out of the fallback buffer
+ ch = fallbackBuffer != null ? fallbackBuffer.InternalGetNextChar() : 0;
+ if (ch > 0)
+ {
+ goto ProcessChar;
+ }
+ }
+ else
+ {
+ // Case of leftover surrogates in the fallback buffer
+ if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
+ {
+ Debug.Assert(ch >= 0xD800 && ch <= 0xDBFF,
+ "[UTF8Encoding.GetBytes]expected high surrogate, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
+
+ int cha = ch;
+
+ ch = fallbackBuffer.InternalGetNextChar();
+
+ if (InRange(ch, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ ch = ch + (cha << 10) + (0x10000 - CharUnicodeInfo.LOW_SURROGATE_START - (CharUnicodeInfo.HIGH_SURROGATE_START << 10));
+ goto EncodeChar;
+ }
+ else if (ch > 0)
+ {
+ goto ProcessChar;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ // attempt to encode the partial surrogate (will fail or ignore)
+ if (ch > 0 && (encoder == null || encoder.MustFlush))
+ goto EncodeChar;
+
+ // We're done
+ break;
+ }
+
+ if (ch > 0)
+ {
+ // We have a high surrogate left over from a previous loop.
+ Debug.Assert(ch >= 0xD800 && ch <= 0xDBFF,
+ "[UTF8Encoding.GetBytes]expected high surrogate, not 0x" + ((int)ch).ToString("X4", CultureInfo.InvariantCulture));
+
+ // use separate helper variables for local contexts so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ int cha = *pSrc;
+
+ // In previous byte, we encountered a high surrogate, so we are expecting a low surrogate here.
+ // if (IsLowSurrogate(cha)) {
+ if (InRange(cha, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ ch = cha + (ch << 10) +
+ (0x10000
+ - CharUnicodeInfo.LOW_SURROGATE_START
+ - (CharUnicodeInfo.HIGH_SURROGATE_START << 10));
+
+ pSrc++;
+ }
+ // else ch is still high surrogate and encoding will fail
+
+ // attempt to encode the surrogate or partial surrogate
+ goto EncodeChar;
+ }
+
+ // If we've used a fallback, then we have to check for it
+ if (fallbackBuffer != null)
+ {
+ ch = fallbackBuffer.InternalGetNextChar();
+ if (ch > 0) goto ProcessChar;
+ }
+
+ // read next char. The JIT optimization seems to be getting confused when
+ // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
+ ch = *pSrc;
+ pSrc++;
+
+ ProcessChar:
+ // if (IsHighSurrogate(ch)) {
+ if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.HIGH_SURROGATE_END))
+ {
+ continue;
+ }
+ // either good char or partial surrogate
+
+ EncodeChar:
+ // throw exception on partial surrogate if necessary
+ // if (IsLowSurrogate(ch) || IsHighSurrogate(ch))
+ if (InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ // Lone surrogates aren't allowed, we have to do fallback for them
+ // Have to make a fallback buffer if we don't have one
+ if (fallbackBuffer == null)
+ {
+ // wait on fallbacks if we can
+ // For fallback we may need a fallback buffer
+ if (baseEncoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = baseEncoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(chars, pEnd, baseEncoder, true);
+ }
+
+ // Do our fallback. Actually we already know its a mixed up surrogate,
+ // so the ref pSrc isn't gonna do anything.
+ pSrcForFallback = pSrc; // Avoid passing pSrc by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(unchecked((char)ch), ref pSrcForFallback);
+ pSrc = pSrcForFallback;
+
+ // Ignore it if we don't throw
+ ch = 0;
+ continue;
+ }
+
+ // Count bytes needed
+ int bytesNeeded = 1;
+ if (ch > 0x7F)
+ {
+ if (ch > 0x7FF)
+ {
+ if (ch > 0xFFFF)
+ {
+ bytesNeeded++; // 4 bytes (surrogate pair)
+ }
+ bytesNeeded++; // 3 bytes (800-FFFF)
+ }
+ bytesNeeded++; // 2 bytes (80-7FF)
+ }
+
+ if (pTarget > pAllocatedBufferEnd - bytesNeeded)
+ {
+ // Left over surrogate from last time will cause pSrc == chars, so we'll throw
+ if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
+ {
+ fallbackBuffer.MovePrevious(); // Didn't use this fallback char
+ if (ch > 0xFFFF)
+ fallbackBuffer.MovePrevious(); // Was surrogate, didn't use 2nd part either
+ }
+ else
+ {
+ pSrc--; // Didn't use this char
+ if (ch > 0xFFFF)
+ pSrc--; // Was surrogate, didn't use 2nd part either
+ }
+ Debug.Assert(pSrc >= chars || pTarget == bytes,
+ "[UTF8Encoding.GetBytes]Expected pSrc to be within buffer or to throw with insufficient room.");
+ ThrowBytesOverflow(encoder, pTarget == bytes); // Throw if we must
+ ch = 0; // Nothing left over (we backed up to start of pair if supplimentary)
+ break;
+ }
+
+ if (ch <= 0x7F)
+ {
+ *pTarget = (byte)ch;
+ }
+ else
+ {
+ // use separate helper variables for local contexts so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ int chb;
+ if (ch <= 0x7FF)
+ {
+ // 2 byte encoding
+ chb = (byte)(unchecked((sbyte)0xC0) | (ch >> 6));
+ }
+ else
+ {
+ if (ch <= 0xFFFF)
+ {
+ chb = (byte)(unchecked((sbyte)0xE0) | (ch >> 12));
+ }
+ else
+ {
+ *pTarget = (byte)(unchecked((sbyte)0xF0) | (ch >> 18));
+ pTarget++;
+
+ chb = unchecked((sbyte)0x80) | (ch >> 12) & 0x3F;
+ }
+ *pTarget = (byte)chb;
+ pTarget++;
+
+ chb = unchecked((sbyte)0x80) | (ch >> 6) & 0x3F;
+ }
+ *pTarget = (byte)chb;
+ pTarget++;
+
+ *pTarget = (byte)(unchecked((sbyte)0x80) | ch & 0x3F);
+ }
+ pTarget++;
+
+
+#if FASTLOOP
+ // If still have fallback don't do fast loop
+ if (fallbackBuffer != null && (ch = fallbackBuffer.InternalGetNextChar()) != 0)
+ goto ProcessChar;
+
+ int availableChars = PtrDiff(pEnd, pSrc);
+ int availableBytes = PtrDiff(pAllocatedBufferEnd, pTarget);
+
+ // don't fall into the fast decoding loop if we don't have enough characters
+ // Note that if we don't have enough bytes, pStop will prevent us from entering the fast loop.
+ if (availableChars <= 13)
+ {
+ // we are hoping for 1 byte per char
+ if (availableBytes < availableChars)
+ {
+ // not enough output room. no pending bits at this point
+ ch = 0;
+ continue;
+ }
+
+ // try to get over the remainder of the ascii characters fast though
+ char* pLocalEnd = pEnd; // hint to get pLocalEnd enregistered
+ while (pSrc < pLocalEnd)
+ {
+ ch = *pSrc;
+ pSrc++;
+
+ // Not ASCII, need more than 1 byte per char
+ if (ch > 0x7F)
+ goto ProcessChar;
+
+ *pTarget = (byte)ch;
+ pTarget++;
+ }
+ // we are done, let ch be 0 to clear encoder
+ ch = 0;
+ break;
+ }
+
+ // we need at least 1 byte per character, but Convert might allow us to convert
+ // only part of the input, so try as much as we can. Reduce charCount if necessary
+ if (availableBytes < availableChars)
+ {
+ availableChars = availableBytes;
+ }
+
+ // FASTLOOP:
+ // - optimistic range checks
+ // - fallbacks to the slow loop for all special cases, exception throwing, etc.
+
+ // To compute the upper bound, assume that all characters are ASCII characters at this point,
+ // the boundary will be decreased for every non-ASCII character we encounter
+ // Also, we need 5 chars reserve for the unrolled ansi decoding loop and for decoding of surrogates
+ // If there aren't enough bytes for the output, then pStop will be <= pSrc and will bypass the loop.
+ char* pStop = pSrc + availableChars - 5;
+
+ while (pSrc < pStop)
+ {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F)
+ {
+ goto LongCode;
+ }
+ *pTarget = (byte)ch;
+ pTarget++;
+
+ // get pSrc aligned
+ if ((unchecked((int)pSrc) & 0x2) != 0)
+ {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F)
+ {
+ goto LongCode;
+ }
+ *pTarget = (byte)ch;
+ pTarget++;
+ }
+
+ // Run 4 characters at a time!
+ while (pSrc < pStop)
+ {
+ ch = *(int*)pSrc;
+ int chc = *(int*)(pSrc + 2);
+ if (((ch | chc) & unchecked((int)0xFF80FF80)) != 0)
+ {
+ goto LongCodeWithMask;
+ }
+
+ // Unfortunately, this is endianess sensitive
+#if BIGENDIAN
+ *pTarget = (byte)(ch>>16);
+ *(pTarget+1) = (byte)ch;
+ pSrc += 4;
+ *(pTarget+2) = (byte)(chc>>16);
+ *(pTarget+3) = (byte)chc;
+ pTarget += 4;
+#else // BIGENDIAN
+ *pTarget = (byte)ch;
+ *(pTarget + 1) = (byte)(ch >> 16);
+ pSrc += 4;
+ *(pTarget + 2) = (byte)chc;
+ *(pTarget + 3) = (byte)(chc >> 16);
+ pTarget += 4;
+#endif // BIGENDIAN
+ }
+ continue;
+
+ LongCodeWithMask:
+#if BIGENDIAN
+ // be careful about the sign extension
+ ch = (int)(((uint)ch) >> 16);
+#else // BIGENDIAN
+ ch = (char)ch;
+#endif // BIGENDIAN
+ pSrc++;
+
+ if (ch > 0x7F)
+ {
+ goto LongCode;
+ }
+ *pTarget = (byte)ch;
+ pTarget++;
+ continue;
+
+ LongCode:
+ // use separate helper variables for slow and fast loop so that the jit optimizations
+ // won't get confused about the variable lifetimes
+ int chd;
+ if (ch <= 0x7FF)
+ {
+ // 2 byte encoding
+ chd = unchecked((sbyte)0xC0) | (ch >> 6);
+ }
+ else
+ {
+ // if (!IsLowSurrogate(ch) && !IsHighSurrogate(ch))
+ if (!InRange(ch, CharUnicodeInfo.HIGH_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ // 3 byte encoding
+ chd = unchecked((sbyte)0xE0) | (ch >> 12);
+ }
+ else
+ {
+ // 4 byte encoding - high surrogate + low surrogate
+ // if (!IsHighSurrogate(ch))
+ if (ch > CharUnicodeInfo.HIGH_SURROGATE_END)
+ {
+ // low without high -> bad, try again in slow loop
+ pSrc -= 1;
+ break;
+ }
+
+ chd = *pSrc;
+ pSrc++;
+
+ // if (!IsLowSurrogate(chd)) {
+ if (!InRange(chd, CharUnicodeInfo.LOW_SURROGATE_START, CharUnicodeInfo.LOW_SURROGATE_END))
+ {
+ // high not followed by low -> bad, try again in slow loop
+ pSrc -= 2;
+ break;
+ }
+
+ ch = chd + (ch << 10) +
+ (0x10000
+ - CharUnicodeInfo.LOW_SURROGATE_START
+ - (CharUnicodeInfo.HIGH_SURROGATE_START << 10));
+
+ *pTarget = (byte)(unchecked((sbyte)0xF0) | (ch >> 18));
+ // pStop - this byte is compensated by the second surrogate character
+ // 2 input chars require 4 output bytes. 2 have been anticipated already
+ // and 2 more will be accounted for by the 2 pStop-- calls below.
+ pTarget++;
+
+ chd = unchecked((sbyte)0x80) | (ch >> 12) & 0x3F;
+ }
+ *pTarget = (byte)chd;
+ pStop--; // 3 byte sequence for 1 char, so need pStop-- and the one below too.
+ pTarget++;
+
+ chd = unchecked((sbyte)0x80) | (ch >> 6) & 0x3F;
+ }
+ *pTarget = (byte)chd;
+ pStop--; // 2 byte sequence for 1 char so need pStop--.
+ pTarget++;
+
+ *pTarget = (byte)(unchecked((sbyte)0x80) | ch & 0x3F);
+ // pStop - this byte is already included
+ pTarget++;
+ }
+
+ Debug.Assert(pTarget <= pAllocatedBufferEnd, "[UTF8Encoding.GetBytes]pTarget <= pAllocatedBufferEnd");
+
+#endif // FASTLOOP
+
+ // no pending char at this point
+ ch = 0;
+ }
+
+ // Do we have to set the encoder bytes?
+ if (encoder != null)
+ {
+ Debug.Assert(!encoder.MustFlush || ch == 0,
+ "[UTF8Encoding.GetBytes] Expected no mustflush or 0 leftover ch " + ch.ToString("X2", CultureInfo.InvariantCulture));
+
+ encoder.surrogateChar = ch;
+ encoder.m_charsUsed = (int)(pSrc - chars);
+ }
+
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 ||
+ baseEncoder == null || !baseEncoder.m_throwOnOverflow,
+ "[UTF8Encoding.GetBytes]Expected empty fallback buffer if not converting");
+
+ return (int)(pTarget - bytes);
+ }
+
+
+ // These are bitmasks used to maintain the state in the decoder. They occupy the higher bits
+ // while the actual character is being built in the lower bits. They are shifted together
+ // with the actual bits of the character.
+
+ // bits 30 & 31 are used for pending bits fixup
+ private const int FinalByte = 1 << 29;
+ private const int SupplimentarySeq = 1 << 28;
+ private const int ThreeByteSeq = 1 << 27;
+
+ // Note: We throw exceptions on individually encoded surrogates and other non-shortest forms.
+ // If exceptions aren't turned on, then we drop all non-shortest &individual surrogates.
+ //
+ // To simplify maintenance, the structure of GetCharCount and GetChars should be
+ // kept the same as much as possible
+ internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
+ {
+ Debug.Assert(count >= 0, "[UTF8Encoding.GetCharCount]count >=0");
+ Debug.Assert(bytes != null, "[UTF8Encoding.GetCharCount]bytes!=null");
+
+ // Initialize stuff
+ byte* pSrc = bytes;
+ byte* pEnd = pSrc + count;
+
+ // Start by assuming we have as many as count, charCount always includes the adjustment
+ // for the character being decoded
+ int charCount = count;
+ int ch = 0;
+ DecoderFallbackBuffer fallback = null;
+
+ if (baseDecoder != null)
+ {
+ UTF8Decoder decoder = (UTF8Decoder)baseDecoder;
+ ch = decoder.bits;
+ charCount -= (ch >> 30); // Adjust char count for # of expected bytes and expected output chars.
+
+ // Shouldn't have anything in fallback buffer for GetCharCount
+ // (don't have to check m_throwOnOverflow for count)
+ Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
+ "[UTF8Encoding.GetCharCount]Expected empty fallback buffer at start");
+ }
+
+ for (;;)
+ {
+ // SLOWLOOP: does all range checks, handles all special cases, but it is slow
+
+ if (pSrc >= pEnd)
+ {
+ break;
+ }
+
+ if (ch == 0)
+ {
+ // no pending bits
+ goto ReadChar;
+ }
+
+ // read next byte. The JIT optimization seems to be getting confused when
+ // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
+ int cha = *pSrc;
+ pSrc++;
+
+ // we are expecting to see trailing bytes like 10vvvvvv
+ if ((cha & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ // This can be a valid starting byte for another UTF8 byte sequence, so let's put
+ // the current byte back, and try to see if this is a valid byte for another UTF8 byte sequence
+ pSrc--;
+ charCount += (ch >> 30);
+ goto InvalidByteSequence;
+ }
+
+ // fold in the new byte
+ ch = (ch << 6) | (cha & 0x3F);
+
+ if ((ch & FinalByte) == 0)
+ {
+ Debug.Assert((ch & (SupplimentarySeq | ThreeByteSeq)) != 0,
+ "[UTF8Encoding.GetChars]Invariant volation");
+
+ if ((ch & SupplimentarySeq) != 0)
+ {
+ if ((ch & (FinalByte >> 6)) != 0)
+ {
+ // this is 3rd byte (of 4 byte supplimentary) - nothing to do
+ continue;
+ }
+
+ // 2nd byte, check for non-shortest form of supplimentary char and the valid
+ // supplimentary characters in range 0x010000 - 0x10FFFF at the same time
+ if (!InRange(ch & 0x1F0, 0x10, 0x100))
+ {
+ goto InvalidByteSequence;
+ }
+ }
+ else
+ {
+ // Must be 2nd byte of a 3-byte sequence
+ // check for non-shortest form of 3 byte seq
+ if ((ch & (0x1F << 5)) == 0 || // non-shortest form
+ (ch & (0xF800 >> 6)) == (0xD800 >> 6)) // illegal individually encoded surrogate
+ {
+ goto InvalidByteSequence;
+ }
+ }
+ continue;
+ }
+
+ // ready to punch
+
+ // adjust for surrogates in non-shortest form
+ if ((ch & (SupplimentarySeq | 0x1F0000)) == SupplimentarySeq)
+ {
+ charCount--;
+ }
+ goto EncodeChar;
+
+ InvalidByteSequence:
+ // this code fragment should be close to the gotos referencing it
+ // Have to do fallback for invalid bytes
+ if (fallback == null)
+ {
+ if (baseDecoder == null)
+ fallback = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallback = baseDecoder.FallbackBuffer;
+ fallback.InternalInitialize(bytes, null);
+ }
+ charCount += FallbackInvalidByteSequence(pSrc, ch, fallback);
+
+ ch = 0;
+ continue;
+
+ ReadChar:
+ ch = *pSrc;
+ pSrc++;
+
+ ProcessChar:
+ if (ch > 0x7F)
+ {
+ // If its > 0x7F, its start of a new multi-byte sequence
+
+ // Long sequence, so unreserve our char.
+ charCount--;
+
+ // bit 6 has to be non-zero for start of multibyte chars.
+ if ((ch & 0x40) == 0)
+ {
+ // Unexpected trail byte
+ goto InvalidByteSequence;
+ }
+
+ // start a new long code
+ if ((ch & 0x20) != 0)
+ {
+ if ((ch & 0x10) != 0)
+ {
+ // 4 byte encoding - supplimentary character (2 surrogates)
+
+ ch &= 0x0F;
+
+ // check that bit 4 is zero and the valid supplimentary character
+ // range 0x000000 - 0x10FFFF at the same time
+ if (ch > 0x04)
+ {
+ ch |= 0xf0;
+ goto InvalidByteSequence;
+ }
+
+ // Add bit flags so that when we check new characters & rotate we'll be flagged correctly.
+ // Final byte flag, count fix if we don't make final byte & supplimentary sequence flag.
+ ch |= (FinalByte >> 3 * 6) | // Final byte is 3 more bytes from now
+ (1 << 30) | // If it dies on next byte we'll need an extra char
+ (3 << (30 - 2 * 6)) | // If it dies on last byte we'll need to subtract a char
+ (SupplimentarySeq) | (SupplimentarySeq >> 6) |
+ (SupplimentarySeq >> 2 * 6) | (SupplimentarySeq >> 3 * 6);
+
+ // Our character count will be 2 characters for these 4 bytes, so subtract another char
+ charCount--;
+ }
+ else
+ {
+ // 3 byte encoding
+ // Add bit flags so that when we check new characters & rotate we'll be flagged correctly.
+ ch = (ch & 0x0F) | ((FinalByte >> 2 * 6) | (1 << 30) |
+ (ThreeByteSeq) | (ThreeByteSeq >> 6) | (ThreeByteSeq >> 2 * 6));
+
+ // We'll expect 1 character for these 3 bytes, so subtract another char.
+ charCount--;
+ }
+ }
+ else
+ {
+ // 2 byte encoding
+
+ ch &= 0x1F;
+
+ // check for non-shortest form
+ if (ch <= 1)
+ {
+ ch |= 0xc0;
+ goto InvalidByteSequence;
+ }
+
+ // Add bit flags so we'll be flagged correctly
+ ch |= (FinalByte >> 6);
+ }
+ continue;
+ }
+
+ EncodeChar:
+
+#if FASTLOOP
+ int availableBytes = PtrDiff(pEnd, pSrc);
+
+ // don't fall into the fast decoding loop if we don't have enough bytes
+ if (availableBytes <= 13)
+ {
+ // try to get over the remainder of the ascii characters fast though
+ byte* pLocalEnd = pEnd; // hint to get pLocalEnd enregistered
+ while (pSrc < pLocalEnd)
+ {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F)
+ goto ProcessChar;
+ }
+ // we are done
+ ch = 0;
+ break;
+ }
+
+ // To compute the upper bound, assume that all characters are ASCII characters at this point,
+ // the boundary will be decreased for every non-ASCII character we encounter
+ // Also, we need 7 chars reserve for the unrolled ansi decoding loop and for decoding of multibyte sequences
+ byte* pStop = pSrc + availableBytes - 7;
+
+ while (pSrc < pStop)
+ {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F)
+ {
+ goto LongCode;
+ }
+
+ // get pSrc 2-byte aligned
+ if ((unchecked((int)pSrc) & 0x1) != 0)
+ {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F)
+ {
+ goto LongCode;
+ }
+ }
+
+ // get pSrc 4-byte aligned
+ if ((unchecked((int)pSrc) & 0x2) != 0)
+ {
+ ch = *(ushort*)pSrc;
+ if ((ch & 0x8080) != 0)
+ {
+ goto LongCodeWithMask16;
+ }
+ pSrc += 2;
+ }
+
+ // Run 8 + 8 characters at a time!
+ while (pSrc < pStop)
+ {
+ ch = *(int*)pSrc;
+ int chb = *(int*)(pSrc + 4);
+ if (((ch | chb) & unchecked((int)0x80808080)) != 0)
+ {
+ goto LongCodeWithMask32;
+ }
+ pSrc += 8;
+
+ // This is a really small loop - unroll it
+ if (pSrc >= pStop)
+ break;
+
+ ch = *(int*)pSrc;
+ chb = *(int*)(pSrc + 4);
+ if (((ch | chb) & unchecked((int)0x80808080)) != 0)
+ {
+ goto LongCodeWithMask32;
+ }
+ pSrc += 8;
+ }
+ break;
+
+#if BIGENDIAN
+ LongCodeWithMask32:
+ // be careful about the sign extension
+ ch = (int)(((uint)ch) >> 16);
+ LongCodeWithMask16:
+ ch = (int)(((uint)ch) >> 8);
+#else // BIGENDIAN
+ LongCodeWithMask32:
+ LongCodeWithMask16:
+ ch &= 0xFF;
+#endif // BIGENDIAN
+ pSrc++;
+ if (ch <= 0x7F)
+ {
+ continue;
+ }
+
+ LongCode:
+ int chc = *pSrc;
+ pSrc++;
+
+ if (
+ // bit 6 has to be zero
+ (ch & 0x40) == 0 ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (chc & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ goto BadLongCode;
+ }
+
+ chc &= 0x3F;
+
+ // start a new long code
+ if ((ch & 0x20) != 0)
+ {
+ // fold the first two bytes together
+ chc |= (ch & 0x0F) << 6;
+
+ if ((ch & 0x10) != 0)
+ {
+ // 4 byte encoding - surrogate
+ ch = *pSrc;
+ if (
+ // check that bit 4 is zero, the non-shortest form of surrogate
+ // and the valid surrogate range 0x000000 - 0x10FFFF at the same time
+ !InRange(chc >> 4, 0x01, 0x10) ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (ch & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ goto BadLongCode;
+ }
+
+ chc = (chc << 6) | (ch & 0x3F);
+
+ ch = *(pSrc + 1);
+ // we are expecting to see trailing bytes like 10vvvvvv
+ if ((ch & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ goto BadLongCode;
+ }
+ pSrc += 2;
+
+ // extra byte
+ charCount--;
+ }
+ else
+ {
+ // 3 byte encoding
+ ch = *pSrc;
+ if (
+ // check for non-shortest form of 3 byte seq
+ (chc & (0x1F << 5)) == 0 ||
+ // Can't have surrogates here.
+ (chc & (0xF800 >> 6)) == (0xD800 >> 6) ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (ch & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ goto BadLongCode;
+ }
+ pSrc++;
+
+ // extra byte
+ charCount--;
+ }
+ }
+ else
+ {
+ // 2 byte encoding
+
+ // check for non-shortest form
+ if ((ch & 0x1E) == 0)
+ {
+ goto BadLongCode;
+ }
+ }
+
+ // extra byte
+ charCount--;
+ }
+#endif // FASTLOOP
+
+ // no pending bits at this point
+ ch = 0;
+ continue;
+
+ BadLongCode:
+ pSrc -= 2;
+ ch = 0;
+ continue;
+ }
+
+ // May have a problem if we have to flush
+ if (ch != 0)
+ {
+ // We were already adjusting for these, so need to unadjust
+ charCount += (ch >> 30);
+ if (baseDecoder == null || baseDecoder.MustFlush)
+ {
+ // Have to do fallback for invalid bytes
+ if (fallback == null)
+ {
+ if (baseDecoder == null)
+ fallback = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallback = baseDecoder.FallbackBuffer;
+ fallback.InternalInitialize(bytes, null);
+ }
+ charCount += FallbackInvalidByteSequence(pSrc, ch, fallback);
+ }
+ }
+
+ // Shouldn't have anything in fallback buffer for GetCharCount
+ // (don't have to check m_throwOnOverflow for count)
+ Debug.Assert(fallback == null || fallback.Remaining == 0,
+ "[UTF8Encoding.GetCharCount]Expected empty fallback buffer at end");
+
+ return charCount;
+ }
+
+ // WARNING: If we throw an error, then System.Resources.ResourceReader calls this method.
+ // So if we're really broken, then that could also throw an error... recursively.
+ // So try to make sure GetChars can at least process all uses by
+ // System.Resources.ResourceReader!
+ //
+ // Note: We throw exceptions on individually encoded surrogates and other non-shortest forms.
+ // If exceptions aren't turned on, then we drop all non-shortest &individual surrogates.
+ //
+ // To simplify maintenance, the structure of GetCharCount and GetChars should be
+ // kept the same as much as possible
+ internal override unsafe int GetChars(byte* bytes, int byteCount,
+ char* chars, int charCount, DecoderNLS baseDecoder)
+ {
+ Debug.Assert(chars != null, "[UTF8Encoding.GetChars]chars!=null");
+ Debug.Assert(byteCount >= 0, "[UTF8Encoding.GetChars]count >=0");
+ Debug.Assert(charCount >= 0, "[UTF8Encoding.GetChars]charCount >=0");
+ Debug.Assert(bytes != null, "[UTF8Encoding.GetChars]bytes!=null");
+
+ byte* pSrc = bytes;
+ char* pTarget = chars;
+
+ byte* pEnd = pSrc + byteCount;
+ char* pAllocatedBufferEnd = pTarget + charCount;
+
+ int ch = 0;
+
+ DecoderFallbackBuffer fallback = null;
+ byte* pSrcForFallback;
+ char* pTargetForFallback;
+ if (baseDecoder != null)
+ {
+ UTF8Decoder decoder = (UTF8Decoder)baseDecoder;
+ ch = decoder.bits;
+
+ // Shouldn't have anything in fallback buffer for GetChars
+ // (don't have to check m_throwOnOverflow for chars, we always use all or none so always should be empty)
+ Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
+ "[UTF8Encoding.GetChars]Expected empty fallback buffer at start");
+ }
+
+ for (;;)
+ {
+ // SLOWLOOP: does all range checks, handles all special cases, but it is slow
+
+ if (pSrc >= pEnd)
+ {
+ break;
+ }
+
+ if (ch == 0)
+ {
+ // no pending bits
+ goto ReadChar;
+ }
+
+ // read next byte. The JIT optimization seems to be getting confused when
+ // compiling "ch = *pSrc++;", so rather use "ch = *pSrc; pSrc++;" instead
+ int cha = *pSrc;
+ pSrc++;
+
+ // we are expecting to see trailing bytes like 10vvvvvv
+ if ((cha & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ // This can be a valid starting byte for another UTF8 byte sequence, so let's put
+ // the current byte back, and try to see if this is a valid byte for another UTF8 byte sequence
+ pSrc--;
+ goto InvalidByteSequence;
+ }
+
+ // fold in the new byte
+ ch = (ch << 6) | (cha & 0x3F);
+
+ if ((ch & FinalByte) == 0)
+ {
+ // Not at last byte yet
+ Debug.Assert((ch & (SupplimentarySeq | ThreeByteSeq)) != 0,
+ "[UTF8Encoding.GetChars]Invariant volation");
+
+ if ((ch & SupplimentarySeq) != 0)
+ {
+ // Its a 4-byte supplimentary sequence
+ if ((ch & (FinalByte >> 6)) != 0)
+ {
+ // this is 3rd byte of 4 byte sequence - nothing to do
+ continue;
+ }
+
+ // 2nd byte of 4 bytes
+ // check for non-shortest form of surrogate and the valid surrogate
+ // range 0x000000 - 0x10FFFF at the same time
+ if (!InRange(ch & 0x1F0, 0x10, 0x100))
+ {
+ goto InvalidByteSequence;
+ }
+ }
+ else
+ {
+ // Must be 2nd byte of a 3-byte sequence
+ // check for non-shortest form of 3 byte seq
+ if ((ch & (0x1F << 5)) == 0 || // non-shortest form
+ (ch & (0xF800 >> 6)) == (0xD800 >> 6)) // illegal individually encoded surrogate
+ {
+ goto InvalidByteSequence;
+ }
+ }
+ continue;
+ }
+
+ // ready to punch
+
+ // surrogate in shortest form?
+ // Might be possible to get rid of this? Already did non-shortest check for 4-byte sequence when reading 2nd byte?
+ if ((ch & (SupplimentarySeq | 0x1F0000)) > SupplimentarySeq)
+ {
+ // let the range check for the second char throw the exception
+ if (pTarget < pAllocatedBufferEnd)
+ {
+ *pTarget = (char)(((ch >> 10) & 0x7FF) +
+ unchecked((short)((CharUnicodeInfo.HIGH_SURROGATE_START - (0x10000 >> 10)))));
+ pTarget++;
+
+ ch = (ch & 0x3FF) +
+ unchecked((int)(CharUnicodeInfo.LOW_SURROGATE_START));
+ }
+ }
+
+ goto EncodeChar;
+
+ InvalidByteSequence:
+ // this code fragment should be close to the gotos referencing it
+ // Have to do fallback for invalid bytes
+ if (fallback == null)
+ {
+ if (baseDecoder == null)
+ fallback = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallback = baseDecoder.FallbackBuffer;
+ fallback.InternalInitialize(bytes, pAllocatedBufferEnd);
+ }
+ // This'll back us up the appropriate # of bytes if we didn't get anywhere
+ pSrcForFallback = pSrc; // Avoid passing pSrc by reference to allow it to be enregistered
+ pTargetForFallback = pTarget; // Avoid passing pTarget by reference to allow it to be enregistered
+ bool fallbackResult = FallbackInvalidByteSequence(ref pSrcForFallback, ch, fallback, ref pTargetForFallback);
+ pSrc = pSrcForFallback;
+ pTarget = pTargetForFallback;
+
+ if (!fallbackResult)
+ {
+ // Ran out of buffer space
+ // Need to throw an exception?
+ Debug.Assert(pSrc >= bytes || pTarget == chars,
+ "[UTF8Encoding.GetChars]Expected to throw or remain in byte buffer after fallback");
+ fallback.InternalReset();
+ ThrowCharsOverflow(baseDecoder, pTarget == chars);
+ ch = 0;
+ break;
+ }
+ Debug.Assert(pSrc >= bytes,
+ "[UTF8Encoding.GetChars]Expected invalid byte sequence to have remained within the byte array");
+ ch = 0;
+ continue;
+
+ ReadChar:
+ ch = *pSrc;
+ pSrc++;
+
+ ProcessChar:
+ if (ch > 0x7F)
+ {
+ // If its > 0x7F, its start of a new multi-byte sequence
+
+ // bit 6 has to be non-zero
+ if ((ch & 0x40) == 0)
+ {
+ goto InvalidByteSequence;
+ }
+
+ // start a new long code
+ if ((ch & 0x20) != 0)
+ {
+ if ((ch & 0x10) != 0)
+ {
+ // 4 byte encoding - supplimentary character (2 surrogates)
+
+ ch &= 0x0F;
+
+ // check that bit 4 is zero and the valid supplimentary character
+ // range 0x000000 - 0x10FFFF at the same time
+ if (ch > 0x04)
+ {
+ ch |= 0xf0;
+ goto InvalidByteSequence;
+ }
+
+ ch |= (FinalByte >> 3 * 6) | (1 << 30) | (3 << (30 - 2 * 6)) |
+ (SupplimentarySeq) | (SupplimentarySeq >> 6) |
+ (SupplimentarySeq >> 2 * 6) | (SupplimentarySeq >> 3 * 6);
+ }
+ else
+ {
+ // 3 byte encoding
+ ch = (ch & 0x0F) | ((FinalByte >> 2 * 6) | (1 << 30) |
+ (ThreeByteSeq) | (ThreeByteSeq >> 6) | (ThreeByteSeq >> 2 * 6));
+ }
+ }
+ else
+ {
+ // 2 byte encoding
+
+ ch &= 0x1F;
+
+ // check for non-shortest form
+ if (ch <= 1)
+ {
+ ch |= 0xc0;
+ goto InvalidByteSequence;
+ }
+
+ ch |= (FinalByte >> 6);
+ }
+ continue;
+ }
+
+ EncodeChar:
+ // write the pending character
+ if (pTarget >= pAllocatedBufferEnd)
+ {
+ // Fix chars so we make sure to throw if we didn't output anything
+ ch &= 0x1fffff;
+ if (ch > 0x7f)
+ {
+ if (ch > 0x7ff)
+ {
+ if (ch >= CharUnicodeInfo.LOW_SURROGATE_START &&
+ ch <= CharUnicodeInfo.LOW_SURROGATE_END)
+ {
+ pSrc--; // It was 4 bytes
+ pTarget--; // 1 was stored already, but we can't remember 1/2, so back up
+ }
+ else if (ch > 0xffff)
+ {
+ pSrc--; // It was 4 bytes, nothing was stored
+ }
+ pSrc--; // It was at least 3 bytes
+ }
+ pSrc--; // It was at least 2 bytes
+ }
+ pSrc--;
+
+ // Throw that we don't have enough room (pSrc could be < chars if we had started to process
+ // a 4 byte sequence alredy)
+ Debug.Assert(pSrc >= bytes || pTarget == chars,
+ "[UTF8Encoding.GetChars]Expected pSrc to be within input buffer or throw due to no output]");
+ ThrowCharsOverflow(baseDecoder, pTarget == chars);
+
+ // Don't store ch in decoder, we already backed up to its start
+ ch = 0;
+
+ // Didn't throw, just use this buffer size.
+ break;
+ }
+ *pTarget = (char)ch;
+ pTarget++;
+
+#if FASTLOOP
+ int availableChars = PtrDiff(pAllocatedBufferEnd, pTarget);
+ int availableBytes = PtrDiff(pEnd, pSrc);
+
+ // don't fall into the fast decoding loop if we don't have enough bytes
+ // Test for availableChars is done because pStop would be <= pTarget.
+ if (availableBytes <= 13)
+ {
+ // we may need as many as 1 character per byte
+ if (availableChars < availableBytes)
+ {
+ // not enough output room. no pending bits at this point
+ ch = 0;
+ continue;
+ }
+
+ // try to get over the remainder of the ascii characters fast though
+ byte* pLocalEnd = pEnd; // hint to get pLocalEnd enregistered
+ while (pSrc < pLocalEnd)
+ {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F)
+ goto ProcessChar;
+
+ *pTarget = (char)ch;
+ pTarget++;
+ }
+ // we are done
+ ch = 0;
+ break;
+ }
+
+ // we may need as many as 1 character per byte, so reduce the byte count if necessary.
+ // If availableChars is too small, pStop will be before pTarget and we won't do fast loop.
+ if (availableChars < availableBytes)
+ {
+ availableBytes = availableChars;
+ }
+
+ // To compute the upper bound, assume that all characters are ASCII characters at this point,
+ // the boundary will be decreased for every non-ASCII character we encounter
+ // Also, we need 7 chars reserve for the unrolled ansi decoding loop and for decoding of multibyte sequences
+ char* pStop = pTarget + availableBytes - 7;
+
+ while (pTarget < pStop)
+ {
+ ch = *pSrc;
+ pSrc++;
+
+ if (ch > 0x7F)
+ {
+ goto LongCode;
+ }
+ *pTarget = (char)ch;
+ pTarget++;
+
+ // get pSrc to be 2-byte aligned
+ if ((unchecked((int)pSrc) & 0x1) != 0)
+ {
+ ch = *pSrc;
+ pSrc++;
+ if (ch > 0x7F)
+ {
+ goto LongCode;
+ }
+ *pTarget = (char)ch;
+ pTarget++;
+ }
+
+ // get pSrc to be 4-byte aligned
+ if ((unchecked((int)pSrc) & 0x2) != 0)
+ {
+ ch = *(ushort*)pSrc;
+ if ((ch & 0x8080) != 0)
+ {
+ goto LongCodeWithMask16;
+ }
+
+ // Unfortunately, this is endianess sensitive
+#if BIGENDIAN
+ *pTarget = (char)((ch >> 8) & 0x7F);
+ pSrc += 2;
+ *(pTarget+1) = (char)(ch & 0x7F);
+ pTarget += 2;
+#else // BIGENDIAN
+ *pTarget = (char)(ch & 0x7F);
+ pSrc += 2;
+ *(pTarget + 1) = (char)((ch >> 8) & 0x7F);
+ pTarget += 2;
+#endif // BIGENDIAN
+ }
+
+ // Run 8 characters at a time!
+ while (pTarget < pStop)
+ {
+ ch = *(int*)pSrc;
+ int chb = *(int*)(pSrc + 4);
+ if (((ch | chb) & unchecked((int)0x80808080)) != 0)
+ {
+ goto LongCodeWithMask32;
+ }
+
+ // Unfortunately, this is endianess sensitive
+#if BIGENDIAN
+ *pTarget = (char)((ch >> 24) & 0x7F);
+ *(pTarget+1) = (char)((ch >> 16) & 0x7F);
+ *(pTarget+2) = (char)((ch >> 8) & 0x7F);
+ *(pTarget+3) = (char)(ch & 0x7F);
+ pSrc += 8;
+ *(pTarget+4) = (char)((chb >> 24) & 0x7F);
+ *(pTarget+5) = (char)((chb >> 16) & 0x7F);
+ *(pTarget+6) = (char)((chb >> 8) & 0x7F);
+ *(pTarget+7) = (char)(chb & 0x7F);
+ pTarget += 8;
+#else // BIGENDIAN
+ *pTarget = (char)(ch & 0x7F);
+ *(pTarget + 1) = (char)((ch >> 8) & 0x7F);
+ *(pTarget + 2) = (char)((ch >> 16) & 0x7F);
+ *(pTarget + 3) = (char)((ch >> 24) & 0x7F);
+ pSrc += 8;
+ *(pTarget + 4) = (char)(chb & 0x7F);
+ *(pTarget + 5) = (char)((chb >> 8) & 0x7F);
+ *(pTarget + 6) = (char)((chb >> 16) & 0x7F);
+ *(pTarget + 7) = (char)((chb >> 24) & 0x7F);
+ pTarget += 8;
+#endif // BIGENDIAN
+ }
+ break;
+
+#if BIGENDIAN
+ LongCodeWithMask32:
+ // be careful about the sign extension
+ ch = (int)(((uint)ch) >> 16);
+ LongCodeWithMask16:
+ ch = (int)(((uint)ch) >> 8);
+#else // BIGENDIAN
+ LongCodeWithMask32:
+ LongCodeWithMask16:
+ ch &= 0xFF;
+#endif // BIGENDIAN
+ pSrc++;
+ if (ch <= 0x7F)
+ {
+ *pTarget = (char)ch;
+ pTarget++;
+ continue;
+ }
+
+ LongCode:
+ int chc = *pSrc;
+ pSrc++;
+
+ if (
+ // bit 6 has to be zero
+ (ch & 0x40) == 0 ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (chc & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ goto BadLongCode;
+ }
+
+ chc &= 0x3F;
+
+ // start a new long code
+ if ((ch & 0x20) != 0)
+ {
+ // fold the first two bytes together
+ chc |= (ch & 0x0F) << 6;
+
+ if ((ch & 0x10) != 0)
+ {
+ // 4 byte encoding - surrogate
+ ch = *pSrc;
+ if (
+ // check that bit 4 is zero, the non-shortest form of surrogate
+ // and the valid surrogate range 0x000000 - 0x10FFFF at the same time
+ !InRange(chc >> 4, 0x01, 0x10) ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (ch & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ goto BadLongCode;
+ }
+
+ chc = (chc << 6) | (ch & 0x3F);
+
+ ch = *(pSrc + 1);
+ // we are expecting to see trailing bytes like 10vvvvvv
+ if ((ch & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ goto BadLongCode;
+ }
+ pSrc += 2;
+
+ ch = (chc << 6) | (ch & 0x3F);
+
+ *pTarget = (char)(((ch >> 10) & 0x7FF) +
+ unchecked((short)(CharUnicodeInfo.HIGH_SURROGATE_START - (0x10000 >> 10))));
+ pTarget++;
+
+ ch = (ch & 0x3FF) +
+ unchecked((short)(CharUnicodeInfo.LOW_SURROGATE_START));
+
+ // extra byte, we're already planning 2 chars for 2 of these bytes,
+ // but the big loop is testing the target against pStop, so we need
+ // to subtract 2 more or we risk overrunning the input. Subtract
+ // one here and one below.
+ pStop--;
+ }
+ else
+ {
+ // 3 byte encoding
+ ch = *pSrc;
+ if (
+ // check for non-shortest form of 3 byte seq
+ (chc & (0x1F << 5)) == 0 ||
+ // Can't have surrogates here.
+ (chc & (0xF800 >> 6)) == (0xD800 >> 6) ||
+ // we are expecting to see trailing bytes like 10vvvvvv
+ (ch & unchecked((sbyte)0xC0)) != 0x80)
+ {
+ goto BadLongCode;
+ }
+ pSrc++;
+
+ ch = (chc << 6) | (ch & 0x3F);
+
+ // extra byte, we're only expecting 1 char for each of these 3 bytes,
+ // but the loop is testing the target (not source) against pStop, so
+ // we need to subtract 2 more or we risk overrunning the input.
+ // Subtract 1 here and one more below
+ pStop--;
+ }
+ }
+ else
+ {
+ // 2 byte encoding
+
+ ch &= 0x1F;
+
+ // check for non-shortest form
+ if (ch <= 1)
+ {
+ goto BadLongCode;
+ }
+ ch = (ch << 6) | chc;
+ }
+
+ *pTarget = (char)ch;
+ pTarget++;
+
+ // extra byte, we're only expecting 1 char for each of these 2 bytes,
+ // but the loop is testing the target (not source) against pStop.
+ // subtract an extra count from pStop so that we don't overrun the input.
+ pStop--;
+ }
+#endif // FASTLOOP
+
+ Debug.Assert(pTarget <= pAllocatedBufferEnd, "[UTF8Encoding.GetChars]pTarget <= pAllocatedBufferEnd");
+
+ // no pending bits at this point
+ ch = 0;
+ continue;
+
+ BadLongCode:
+ pSrc -= 2;
+ ch = 0;
+ continue;
+ }
+
+ if (ch != 0 && (baseDecoder == null || baseDecoder.MustFlush))
+ {
+ // Have to do fallback for invalid bytes
+ if (fallback == null)
+ {
+ if (baseDecoder == null)
+ fallback = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallback = baseDecoder.FallbackBuffer;
+ fallback.InternalInitialize(bytes, pAllocatedBufferEnd);
+ }
+
+ // This'll back us up the appropriate # of bytes if we didn't get anywhere
+ pSrcForFallback = pSrc; // Avoid passing pSrc by reference to allow it to be enregistered
+ pTargetForFallback = pTarget; // Avoid passing pTarget by reference to allow it to be enregistered
+ bool fallbackResult = FallbackInvalidByteSequence(ref pSrcForFallback, ch, fallback, ref pTargetForFallback);
+ pSrc = pSrcForFallback;
+ pTarget = pTargetForFallback;
+
+ if (!fallbackResult)
+ {
+ Debug.Assert(pSrc >= bytes || pTarget == chars,
+ "[UTF8Encoding.GetChars]Expected to throw or remain in byte buffer while flushing");
+
+ // Ran out of buffer space
+ // Need to throw an exception?
+ fallback.InternalReset();
+ ThrowCharsOverflow(baseDecoder, pTarget == chars);
+ }
+ Debug.Assert(pSrc >= bytes,
+ "[UTF8Encoding.GetChars]Expected flushing invalid byte sequence to have remained within the byte array");
+ ch = 0;
+ }
+
+ if (baseDecoder != null)
+ {
+ UTF8Decoder decoder = (UTF8Decoder)baseDecoder;
+
+ // If we're storing flush data we expect all bits to be used or else
+ // we're stuck in the middle of a conversion
+ Debug.Assert(!baseDecoder.MustFlush || ch == 0 || !baseDecoder.m_throwOnOverflow,
+ "[UTF8Encoding.GetChars]Expected no must flush or no left over bits or no throw on overflow.");
+
+ // Remember our leftover bits.
+ decoder.bits = ch;
+
+ baseDecoder.m_bytesUsed = (int)(pSrc - bytes);
+ }
+
+ // Shouldn't have anything in fallback buffer for GetChars
+ // (don't have to check m_throwOnOverflow for chars)
+ Debug.Assert(fallback == null || fallback.Remaining == 0,
+ "[UTF8Encoding.GetChars]Expected empty fallback buffer at end");
+
+ return PtrDiff(pTarget, chars);
+ }
+
+ // During GetChars we had an invalid byte sequence
+ // pSrc is backed up to the start of the bad sequence if we didn't have room to
+ // fall it back. Otherwise pSrc remains wher it is.
+ private unsafe bool FallbackInvalidByteSequence(
+ ref byte* pSrc, int ch, DecoderFallbackBuffer fallback, ref char* pTarget)
+ {
+ // Get our byte[]
+ byte* pStart = pSrc;
+ byte[] bytesUnknown = GetBytesUnknown(ref pStart, ch);
+
+ // Do the actual fallback
+ if (!fallback.InternalFallback(bytesUnknown, pSrc, ref pTarget))
+ {
+ // Oops, it failed, back up to pStart
+ pSrc = pStart;
+ return false;
+ }
+
+ // It worked
+ return true;
+ }
+
+ // During GetCharCount we had an invalid byte sequence
+ // pSrc is used to find the index that points to the invalid bytes,
+ // however the byte[] contains the fallback bytes (in case the index is -1)
+ private unsafe int FallbackInvalidByteSequence(
+ byte* pSrc, int ch, DecoderFallbackBuffer fallback)
+ {
+ // Get our byte[]
+ byte[] bytesUnknown = GetBytesUnknown(ref pSrc, ch);
+
+ // Do the actual fallback
+ int count = fallback.InternalFallback(bytesUnknown, pSrc);
+
+ // # of fallback chars expected.
+ // Note that we only get here for "long" sequences, and have already unreserved
+ // the count that we prereserved for the input bytes
+ return count;
+ }
+
+ // Note that some of these bytes may have come from a previous fallback, so we cannot
+ // just decrement the pointer and use the values we read. In those cases we have
+ // to regenerate the original values.
+ private unsafe byte[] GetBytesUnknown(ref byte* pSrc, int ch)
+ {
+ // Get our byte[]
+ byte[] bytesUnknown = null;
+
+ // See if it was a plain char
+ // (have to check >= 0 because we have all sorts of wierd bit flags)
+ if (ch < 0x100 && ch >= 0)
+ {
+ pSrc--;
+ bytesUnknown = new byte[] { unchecked((byte)ch) };
+ }
+ // See if its an unfinished 2 byte sequence
+ else if ((ch & (SupplimentarySeq | ThreeByteSeq)) == 0)
+ {
+ pSrc--;
+ bytesUnknown = new byte[] { unchecked((byte)((ch & 0x1F) | 0xc0)) };
+ }
+ // So now we're either 2nd byte of 3 or 4 byte sequence or
+ // we hit a non-trail byte or we ran out of space for 3rd byte of 4 byte sequence
+ // 1st check if its a 4 byte sequence
+ else if ((ch & SupplimentarySeq) != 0)
+ {
+ // 3rd byte of 4 byte sequence?
+ if ((ch & (FinalByte >> 6)) != 0)
+ {
+ // 3rd byte of 4 byte sequence
+ pSrc -= 3;
+ bytesUnknown = new byte[] {
+ unchecked((byte)(((ch >> 12) & 0x07) | 0xF0)),
+ unchecked((byte)(((ch >> 6) & 0x3F) | 0x80)),
+ unchecked((byte)(((ch) & 0x3F) | 0x80)) };
+ }
+ else if ((ch & (FinalByte >> 12)) != 0)
+ {
+ // 2nd byte of a 4 byte sequence
+ pSrc -= 2;
+ bytesUnknown = new byte[] {
+ unchecked((byte)(((ch >> 6) & 0x07) | 0xF0)),
+ unchecked((byte)(((ch) & 0x3F) | 0x80)) };
+ }
+ else
+ {
+ // 4th byte of a 4 byte sequence
+ pSrc--;
+ bytesUnknown = new byte[] { unchecked((byte)(((ch) & 0x07) | 0xF0)) };
+ }
+ }
+ else
+ {
+ // 2nd byte of 3 byte sequence?
+ if ((ch & (FinalByte >> 6)) != 0)
+ {
+ // So its 2nd byte of a 3 byte sequence
+ pSrc -= 2;
+ bytesUnknown = new byte[] {
+ unchecked((byte)(((ch >> 6) & 0x0F) | 0xE0)), unchecked ((byte)(((ch) & 0x3F) | 0x80)) };
+ }
+ else
+ {
+ // 1st byte of a 3 byte sequence
+ pSrc--;
+ bytesUnknown = new byte[] { unchecked((byte)(((ch) & 0x0F) | 0xE0)) };
+ }
+ }
+
+ return bytesUnknown;
+ }
+
+
+ public override Decoder GetDecoder()
+ {
+ return new UTF8Decoder(this);
+ }
+
+
+ public override Encoder GetEncoder()
+ {
+ return new UTF8Encoder(this);
+ }
+
+
+ public override int GetMaxByteCount(int charCount)
+ {
+ if (charCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(charCount),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback
+ long byteCount = (long)charCount + 1;
+
+ if (EncoderFallback.MaxCharCount > 1)
+ byteCount *= EncoderFallback.MaxCharCount;
+
+ // Max 3 bytes per char. (4 bytes per 2 chars for surrogates)
+ byteCount *= 3;
+
+ if (byteCount > 0x7fffffff)
+ throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
+
+ return (int)byteCount;
+ }
+
+
+ public override int GetMaxCharCount(int byteCount)
+ {
+ if (byteCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(byteCount),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Figure out our length, 1 char per input byte + 1 char if 1st byte is last byte of 4 byte surrogate pair
+ long charCount = ((long)byteCount + 1);
+
+ // Non-shortest form would fall back, so get max count from fallback.
+ // So would 11... followed by 11..., so you could fall back every byte
+ if (DecoderFallback.MaxCharCount > 1)
+ {
+ charCount *= DecoderFallback.MaxCharCount;
+ }
+
+ if (charCount > 0x7fffffff)
+ throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow);
+
+ return (int)charCount;
+ }
+
+
+ public override byte[] GetPreamble()
+ {
+ if (_emitUTF8Identifier)
+ {
+ // Allocate new array to prevent users from modifying it.
+ return new byte[3] { 0xEF, 0xBB, 0xBF };
+ }
+ else
+ return Array.Empty<byte>();
+ }
+
+
+ public override bool Equals(Object value)
+ {
+ UTF8Encoding that = value as UTF8Encoding;
+ if (that != null)
+ {
+ return (_emitUTF8Identifier == that._emitUTF8Identifier) &&
+ (EncoderFallback.Equals(that.EncoderFallback)) &&
+ (DecoderFallback.Equals(that.DecoderFallback));
+ }
+ return (false);
+ }
+
+
+ public override int GetHashCode()
+ {
+ //Not great distribution, but this is relatively unlikely to be used as the key in a hashtable.
+ return this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode() +
+ UTF8_CODEPAGE + (_emitUTF8Identifier ? 1 : 0);
+ }
+
+ [Serializable]
+ private sealed class UTF8Encoder : EncoderNLS, ISerializable
+ {
+ // We must save a high surrogate value until the next call, looking
+ // for a low surrogate value.
+ internal int surrogateChar;
+
+ public UTF8Encoder(UTF8Encoding encoding) : base(encoding)
+ {
+ // base calls reset
+ }
+
+ // Constructor called by serialization, have to handle deserializing from Everett
+ internal UTF8Encoder(SerializationInfo info, StreamingContext context)
+ {
+ // Any info?
+ if (info == null) throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ // Get common info
+ this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding));
+
+ // SurrogateChar happens to mean the same thing
+ this.surrogateChar = (int)info.GetValue("surrogateChar", typeof(int));
+
+ try
+ {
+ this.m_fallback = (EncoderFallback)info.GetValue("m_fallback", typeof(EncoderFallback));
+ }
+ catch (SerializationException)
+ {
+ this.m_fallback = null;
+ }
+ }
+
+ // ISerializable implementation, get data for this object
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ // Any info?
+ if (info == null) throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ // Save Whidbey data
+ // Just need Everett maxCharSize (BaseCodePageEncoding) or m_maxByteSize (MLangBaseCodePageEncoding)
+ info.AddValue("encoding", this.m_encoding);
+ info.AddValue("surrogateChar", this.surrogateChar);
+
+ info.AddValue("m_fallback", this.m_fallback);
+
+ // Extra stuff for Everett that Whidbey doesn't use
+ info.AddValue("storedSurrogate", this.surrogateChar > 0 ? true : false);
+ info.AddValue("mustFlush", false); // Everett doesn't actually use this either, but it accidently serialized it!
+ }
+
+ public override void Reset()
+
+ {
+ this.surrogateChar = 0;
+ if (m_fallbackBuffer != null)
+ m_fallbackBuffer.Reset();
+ }
+
+ // Anything left in our encoder?
+ internal override bool HasState
+ {
+ get
+ {
+ return (this.surrogateChar != 0);
+ }
+ }
+ }
+
+ [Serializable]
+ private sealed class UTF8Decoder : DecoderNLS, ISerializable
+ {
+ // We'll need to remember the previous information. See the comments around definition
+ // of FinalByte for details.
+ internal int bits;
+
+ public UTF8Decoder(UTF8Encoding encoding) : base(encoding)
+ {
+ // base calls reset
+ }
+
+ // Constructor called by serialization, have to handle deserializing from Everett
+ internal UTF8Decoder(SerializationInfo info, StreamingContext context)
+ {
+ // Any info?
+ if (info == null) throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ // Get common info
+ this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding));
+
+ try
+ {
+ // Get whidbey version of bits
+ this.bits = (int)info.GetValue("wbits", typeof(int));
+ this.m_fallback = (DecoderFallback)info.GetValue("m_fallback", typeof(DecoderFallback));
+ }
+ catch (SerializationException)
+ {
+ // Everett calls bits bits instead of wbits, so this is Everett
+ this.bits = 0;
+ this.m_fallback = null;
+ }
+ }
+
+ // ISerializable implementation, get data for this object
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ // Any info?
+ if (info == null) throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ // Save new Whidbey data
+ info.AddValue("encoding", this.m_encoding);
+ info.AddValue("wbits", this.bits); // Special whidbey bits name
+ info.AddValue("m_fallback", this.m_fallback);
+
+ // Everett has extra stuff, we set it all to 0 in case this deserializes in Everett
+ info.AddValue("bits", (int)0);
+ info.AddValue("trailCount", (int)0);
+ info.AddValue("isSurrogate", false);
+ info.AddValue("byteSequence", (int)0);
+ }
+
+ public override void Reset()
+ {
+ this.bits = 0;
+ if (m_fallbackBuffer != null)
+ m_fallbackBuffer.Reset();
+ }
+
+ // Anything left in our decoder?
+ internal override bool HasState
+ {
+ get
+ {
+ return (this.bits != 0);
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Text/UnicodeEncoding.cs b/src/mscorlib/shared/System/Text/UnicodeEncoding.cs
new file mode 100644
index 0000000000..0e4db9aaad
--- /dev/null
+++ b/src/mscorlib/shared/System/Text/UnicodeEncoding.cs
@@ -0,0 +1,2058 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+// Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused.
+//
+
+using System;
+using System.Globalization;
+using System.Runtime.Serialization;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+
+namespace System.Text
+{
+ [Serializable]
+ public class UnicodeEncoding : Encoding
+ {
+ // Used by Encoding.BigEndianUnicode/Unicode for lazy initialization
+ // The initialization code will not be run until a static member of the class is referenced
+ internal static readonly UnicodeEncoding s_bigEndianDefault = new UnicodeEncoding(bigEndian: true, byteOrderMark: true);
+ internal static readonly UnicodeEncoding s_littleEndianDefault = new UnicodeEncoding(bigEndian: false, byteOrderMark: true);
+
+ [OptionalField(VersionAdded = 2)]
+ internal bool isThrowException = false;
+
+ internal bool bigEndian = false;
+ internal bool byteOrderMark = true;
+
+ // Unicode version 2.0 character size in bytes
+ public const int CharSize = 2;
+
+
+ public UnicodeEncoding()
+ : this(false, true)
+ {
+ }
+
+
+ public UnicodeEncoding(bool bigEndian, bool byteOrderMark)
+ : this(bigEndian, byteOrderMark, false)
+ {
+ }
+
+
+ public UnicodeEncoding(bool bigEndian, bool byteOrderMark, bool throwOnInvalidBytes)
+ : base(bigEndian ? 1201 : 1200) //Set the data item.
+ {
+ this.isThrowException = throwOnInvalidBytes;
+ this.bigEndian = bigEndian;
+ this.byteOrderMark = byteOrderMark;
+
+ // Encoding constructor already did this, but it'll be wrong if we're throwing exceptions
+ if (this.isThrowException)
+ SetDefaultFallbacks();
+ }
+
+ #region Serialization
+ [OnDeserializing]
+ private void OnDeserializing(StreamingContext ctx)
+ {
+ // In Everett it is false. Whidbey will overwrite this value.
+ isThrowException = false;
+ }
+ #endregion Serialization
+
+ internal override void SetDefaultFallbacks()
+ {
+ // For UTF-X encodings, we use a replacement fallback with an empty string
+ if (this.isThrowException)
+ {
+ this.encoderFallback = EncoderFallback.ExceptionFallback;
+ this.decoderFallback = DecoderFallback.ExceptionFallback;
+ }
+ else
+ {
+ this.encoderFallback = new EncoderReplacementFallback("\xFFFD");
+ this.decoderFallback = new DecoderReplacementFallback("\xFFFD");
+ }
+ }
+
+ // The following methods are copied from EncodingNLS.cs.
+ // Unfortunately EncodingNLS.cs is internal and we're public, so we have to reimpliment them here.
+ // These should be kept in sync for the following classes:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ //
+
+ // Returns the number of bytes required to encode a range of characters in
+ // a character array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetByteCount(char[] chars, int index, int count)
+ {
+ // Validate input parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - index < count)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input, return 0, avoid fixed empty array problem
+ if (count == 0)
+ return 0;
+
+ // Just call the pointer version
+ fixed (char* pChars = chars)
+ return GetByteCount(pChars + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetByteCount(String s)
+ {
+ // Validate input
+ if (s==null)
+ throw new ArgumentNullException("s");
+ Contract.EndContractBlock();
+
+ fixed (char* pChars = s)
+ return GetByteCount(pChars, s.Length, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetByteCount(char* chars, int count)
+ {
+ // Validate Parameters
+ if (chars == null)
+ throw new ArgumentNullException("chars", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Call it with empty encoder
+ return GetByteCount(chars, count, null);
+ }
+
+ // Parent method is safe.
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ public override unsafe int GetBytes(String s, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ if (s == null || bytes == null)
+ throw new ArgumentNullException((s == null ? "s" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (s.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("s", SR.ArgumentOutOfRange_IndexCount);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like 0 length arrays.
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = s) fixed (byte* pBytes = &bytes[0])
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // Encodes a range of characters in a character array into a range of bytes
+ // in a byte array. An exception occurs if the byte array is not large
+ // enough to hold the complete encoding of the characters. The
+ // GetByteCount method can be used to determine the exact number of
+ // bytes that will be produced for a given range of characters.
+ // Alternatively, the GetMaxByteCount method can be used to
+ // determine the maximum number of bytes that will be produced for a given
+ // number of characters, regardless of the actual character values.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetBytes(char[] chars, int charIndex, int charCount,
+ byte[] bytes, int byteIndex)
+ {
+ // Validate parameters
+ if (chars == null || bytes == null)
+ throw new ArgumentNullException((chars == null ? "chars" : "bytes"), SR.ArgumentNull_Array);
+
+ if (charIndex < 0 || charCount < 0)
+ throw new ArgumentOutOfRangeException((charIndex < 0 ? "charIndex" : "charCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (chars.Length - charIndex < charCount)
+ throw new ArgumentOutOfRangeException("chars", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (byteIndex < 0 || byteIndex > bytes.Length)
+ throw new ArgumentOutOfRangeException("byteIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If nothing to encode return 0, avoid fixed problem
+ if (charCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int byteCount = bytes.Length - byteIndex;
+
+ // Fixed doesn't like 0 length arrays.
+ if (bytes.Length == 0)
+ bytes = new byte[1];
+
+ fixed (char* pChars = chars) fixed (byte* pBytes = &bytes[0])
+ // Remember that byteCount is # to decode, not size of array.
+ return GetBytes(pChars + charIndex, charCount, pBytes + byteIndex, byteCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetBytes(chars, charCount, bytes, byteCount, null);
+ }
+
+ // Returns the number of characters produced by decoding a range of bytes
+ // in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetCharCount(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - index < count)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // If no input just return 0, fixed doesn't like 0 length arrays
+ if (count == 0)
+ return 0;
+
+ // Just call pointer version
+ fixed (byte* pBytes = bytes)
+ return GetCharCount(pBytes + index, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public override unsafe int GetCharCount(byte* bytes, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (count < 0)
+ throw new ArgumentOutOfRangeException("count", SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetCharCount(bytes, count, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount,
+ char[] chars, int charIndex)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (byteIndex < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((byteIndex < 0 ? "byteIndex" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if ( bytes.Length - byteIndex < byteCount)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+
+ if (charIndex < 0 || charIndex > chars.Length)
+ throw new ArgumentOutOfRangeException("charIndex", SR.ArgumentOutOfRange_Index);
+ Contract.EndContractBlock();
+
+ // If no input, return 0 & avoid fixed problem
+ if (byteCount == 0)
+ return 0;
+
+ // Just call pointer version
+ int charCount = chars.Length - charIndex;
+
+ // Fixed doesn't like 0 length arrays.
+ if (chars.Length == 0)
+ chars = new char[1];
+
+ fixed (byte* pBytes = bytes) fixed (char* pChars = &chars[0])
+ // Remember that charCount is # to decode, not size of array
+ return GetChars(pBytes + byteIndex, byteCount, pChars + charIndex, charCount, null);
+ }
+
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+
+ [CLSCompliant(false)]
+ public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount)
+ {
+ // Validate Parameters
+ if (bytes == null || chars == null)
+ throw new ArgumentNullException(bytes == null ? "bytes" : "chars", SR.ArgumentNull_Array);
+
+ if (charCount < 0 || byteCount < 0)
+ throw new ArgumentOutOfRangeException((charCount < 0 ? "charCount" : "byteCount"), SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ return GetChars(bytes, byteCount, chars, charCount, null);
+ }
+
+ // Returns a string containing the decoded representation of a range of
+ // bytes in a byte array.
+ //
+ // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS)
+ // So if you fix this, fix the others. Currently those include:
+ // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding
+ // parent method is safe
+
+ public override unsafe string GetString(byte[] bytes, int index, int count)
+ {
+ // Validate Parameters
+ if (bytes == null)
+ throw new ArgumentNullException("bytes", SR.ArgumentNull_Array);
+
+ if (index < 0 || count < 0)
+ throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), SR.ArgumentOutOfRange_NeedNonNegNum);
+
+ if (bytes.Length - index < count)
+ throw new ArgumentOutOfRangeException("bytes", SR.ArgumentOutOfRange_IndexCountBuffer);
+ Contract.EndContractBlock();
+
+ // Avoid problems with empty input buffer
+ if (count == 0) return String.Empty;
+
+ fixed (byte* pBytes = bytes)
+ return String.CreateStringFromEncoding(
+ pBytes + index, count, this);
+ }
+
+ //
+ // End of standard methods copied from EncodingNLS.cs
+ //
+
+ internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS encoder)
+ {
+ Debug.Assert(chars != null, "[UnicodeEncoding.GetByteCount]chars!=null");
+ Debug.Assert(count >= 0, "[UnicodeEncoding.GetByteCount]count >=0");
+
+ // Start by assuming each char gets 2 bytes
+ int byteCount = count << 1;
+
+ // Check for overflow in byteCount
+ // (If they were all invalid chars, this would actually be wrong,
+ // but that's a ridiculously large # so we're not concerned about that case)
+ if (byteCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_GetByteCountOverflow);
+
+ char* charStart = chars;
+ char* charEnd = chars + count;
+ char charLeftOver = (char)0;
+
+ bool wasHereBefore = false;
+
+ // Need -1 to check 2 at a time. If we have an even #, longChars will go
+ // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars
+ // will go from longEnd - 1 long to longEnd. (Might not get to use this)
+ ulong* longEnd = (ulong*)(charEnd - 3);
+
+ // For fallback we may need a fallback buffer
+ EncoderFallbackBuffer fallbackBuffer = null;
+ char* charsForFallback;
+
+ if (encoder != null)
+ {
+ charLeftOver = encoder.charLeftOver;
+
+ // Assume extra bytes to encode charLeftOver if it existed
+ if (charLeftOver > 0)
+ byteCount += 2;
+
+ // We mustn't have left over fallback data when counting
+ if (encoder.InternalHasFallbackBuffer)
+ {
+ fallbackBuffer = encoder.FallbackBuffer;
+ if (fallbackBuffer.Remaining > 0)
+ throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
+ }
+ }
+
+ char ch;
+ TryAgain:
+
+ while (((ch = (fallbackBuffer == null) ? (char)0 : fallbackBuffer.InternalGetNextChar()) != 0) || chars < charEnd)
+ {
+ // First unwind any fallback
+ if (ch == 0)
+ {
+ // No fallback, maybe we can do it fast
+#if !NO_FAST_UNICODE_LOOP
+#if BIGENDIAN // If endianess is backwards then each pair of bytes would be backwards.
+ if ( bigEndian &&
+#else
+ if (!bigEndian &&
+#endif // BIGENDIAN
+
+#if BIT64 // 64 bit CPU needs to be long aligned for this to work.
+ charLeftOver == 0 && (unchecked((long)chars) & 7) == 0)
+#else
+ charLeftOver == 0 && (unchecked((int)chars) & 3) == 0)
+#endif
+ {
+ // Need new char* so we can check 4 at a time
+ ulong* longChars = (ulong*)chars;
+
+ while (longChars < longEnd)
+ {
+ // See if we potentially have surrogates (0x8000 bit set)
+ // (We're either big endian on a big endian machine or little endian on
+ // a little endian machine so this'll work)
+ if ((0x8000800080008000 & *longChars) != 0)
+ {
+ // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high
+ // 5 bits looks like 11011, then its a high or low surrogate.
+ // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
+ // Note that we expect BMP characters to be more common than surrogates
+ // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates
+ ulong uTemp = (0xf800f800f800f800 & *longChars) ^ 0xd800d800d800d800;
+
+ // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate
+ // but no clue if they're high or low.
+ // If each of the 4 characters are non-zero, then none are surrogates.
+ if ((uTemp & 0xFFFF000000000000) == 0 ||
+ (uTemp & 0x0000FFFF00000000) == 0 ||
+ (uTemp & 0x00000000FFFF0000) == 0 ||
+ (uTemp & 0x000000000000FFFF) == 0)
+ {
+ // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
+ // or if there's 1 or 4 surrogates
+
+ // If they happen to be high/low/high/low, we may as well continue. Check the next
+ // bit to see if its set (low) or not (high) in the right pattern
+#if BIGENDIAN
+ if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xd800dc00d800dc00) != 0)
+#else
+ if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xdc00d800dc00d800) != 0)
+#endif
+ {
+ // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
+ // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
+
+ // Drop out to the slow loop to resolve the surrogates
+ break;
+ }
+ // else they are all surrogates in High/Low/High/Low order, so we can use them.
+ }
+ // else none are surrogates, so we can use them.
+ }
+ // else all < 0x8000 so we can use them
+
+ // We already counted these four chars, go to next long.
+ longChars++;
+ }
+
+ chars = (char*)longChars;
+
+ if (chars >= charEnd)
+ break;
+ }
+#endif // !NO_FAST_UNICODE_LOOP
+
+ // No fallback, just get next char
+ ch = *chars;
+ chars++;
+ }
+ else
+ {
+ // We weren't preallocating fallback space.
+ byteCount += 2;
+ }
+
+ // Check for high or low surrogates
+ if (ch >= 0xd800 && ch <= 0xdfff)
+ {
+ // Was it a high surrogate?
+ if (ch <= 0xdbff)
+ {
+ // Its a high surrogate, if we already had a high surrogate do its fallback
+ if (charLeftOver > 0)
+ {
+ // Unwind the current character, this should be safe because we
+ // don't have leftover data in the fallback, so chars must have
+ // advanced already.
+ Debug.Assert(chars > charStart,
+ "[UnicodeEncoding.GetByteCount]Expected chars to have advanced in unexpected high surrogate");
+ chars--;
+
+ // If previous high surrogate deallocate 2 bytes
+ byteCount -= 2;
+
+ // Fallback the previous surrogate
+ // Need to initialize fallback buffer?
+ if (fallbackBuffer == null)
+ {
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
+ }
+
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
+ chars = charsForFallback;
+
+ // Now no high surrogate left over
+ charLeftOver = (char)0;
+ continue;
+ }
+
+ // Remember this high surrogate
+ charLeftOver = ch;
+ continue;
+ }
+
+
+ // Its a low surrogate
+ if (charLeftOver == 0)
+ {
+ // Expected a previous high surrogate.
+ // Don't count this one (we'll count its fallback if necessary)
+ byteCount -= 2;
+
+ // fallback this one
+ // Need to initialize fallback buffer?
+ if (fallbackBuffer == null)
+ {
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
+ }
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(ch, ref charsForFallback);
+ chars = charsForFallback;
+ continue;
+ }
+
+ // Valid surrogate pair, add our charLeftOver
+ charLeftOver = (char)0;
+ continue;
+ }
+ else if (charLeftOver > 0)
+ {
+ // Expected a low surrogate, but this char is normal
+
+ // Rewind the current character, fallback previous character.
+ // this should be safe because we don't have leftover data in the
+ // fallback, so chars must have advanced already.
+ Debug.Assert(chars > charStart,
+ "[UnicodeEncoding.GetByteCount]Expected chars to have advanced when expected low surrogate");
+ chars--;
+
+ // fallback previous chars
+ // Need to initialize fallback buffer?
+ if (fallbackBuffer == null)
+ {
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
+ }
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
+ chars = charsForFallback;
+
+ // Ignore charLeftOver or throw
+ byteCount -= 2;
+ charLeftOver = (char)0;
+
+ continue;
+ }
+
+ // Ok we had something to add (already counted)
+ }
+
+ // Don't allocate space for left over char
+ if (charLeftOver > 0)
+ {
+ byteCount -= 2;
+
+ // If we have to flush, stick it in fallback and try again
+ if (encoder == null || encoder.MustFlush)
+ {
+ if (wasHereBefore)
+ {
+ // Throw it, using our complete character
+ throw new ArgumentException(
+ SR.Format(SR.Argument_RecursiveFallback, charLeftOver), nameof(chars));
+ }
+ else
+ {
+ // Need to initialize fallback buffer?
+ if (fallbackBuffer == null)
+ {
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
+ }
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
+ chars = charsForFallback;
+ charLeftOver = (char)0;
+ wasHereBefore = true;
+ goto TryAgain;
+ }
+ }
+ }
+
+ // Shouldn't have anything in fallback buffer for GetByteCount
+ // (don't have to check m_throwOnOverflow for count)
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
+ "[UnicodeEncoding.GetByteCount]Expected empty fallback buffer at end");
+
+ // Don't remember fallbackBuffer.encoder for counting
+ return byteCount;
+ }
+
+ internal override unsafe int GetBytes(char* chars, int charCount,
+ byte* bytes, int byteCount, EncoderNLS encoder)
+ {
+ Debug.Assert(chars != null, "[UnicodeEncoding.GetBytes]chars!=null");
+ Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetBytes]byteCount >=0");
+ Debug.Assert(charCount >= 0, "[UnicodeEncoding.GetBytes]charCount >=0");
+ Debug.Assert(bytes != null, "[UnicodeEncoding.GetBytes]bytes!=null");
+
+ char charLeftOver = (char)0;
+ char ch;
+ bool wasHereBefore = false;
+
+
+ byte* byteEnd = bytes + byteCount;
+ char* charEnd = chars + charCount;
+ byte* byteStart = bytes;
+ char* charStart = chars;
+
+ // For fallback we may need a fallback buffer
+ EncoderFallbackBuffer fallbackBuffer = null;
+ char* charsForFallback;
+
+ // Get our encoder, but don't clear it yet.
+ if (encoder != null)
+ {
+ charLeftOver = encoder.charLeftOver;
+
+ // We mustn't have left over fallback data when counting
+ if (encoder.InternalHasFallbackBuffer)
+ {
+ // We always need the fallback buffer in get bytes so we can flush any remaining ones if necessary
+ fallbackBuffer = encoder.FallbackBuffer;
+ if (fallbackBuffer.Remaining > 0 && encoder.m_throwOnOverflow)
+ throw new ArgumentException(SR.Format(SR.Argument_EncoderFallbackNotEmpty, this.EncodingName, encoder.Fallback.GetType()));
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, false);
+ }
+ }
+
+ TryAgain:
+ while (((ch = (fallbackBuffer == null) ?
+ (char)0 : fallbackBuffer.InternalGetNextChar()) != 0) ||
+ chars < charEnd)
+ {
+ // First unwind any fallback
+ if (ch == 0)
+ {
+ // No fallback, maybe we can do it fast
+#if !NO_FAST_UNICODE_LOOP
+#if BIGENDIAN // If endianess is backwards then each pair of bytes would be backwards.
+ if ( bigEndian &&
+#else
+ if (!bigEndian &&
+#endif // BIGENDIAN
+#if BIT64 // 64 bit CPU needs to be long aligned for this to work, 32 bit CPU needs to be 32 bit aligned
+ (unchecked((long)chars) & 7) == 0 && (unchecked((long)bytes) & 7) == 0 &&
+#else
+ (unchecked((int)chars) & 3) == 0 && (unchecked((int)bytes) & 3) == 0 &&
+#endif // BIT64
+ charLeftOver == 0)
+ {
+ // Need -1 to check 2 at a time. If we have an even #, longChars will go
+ // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars
+ // will go from longEnd - 1 long to longEnd. (Might not get to use this)
+ // We can only go iCount units (limited by shorter of char or byte buffers.
+ ulong* longEnd = (ulong*)(chars - 3 +
+ (((byteEnd - bytes) >> 1 < charEnd - chars) ?
+ (byteEnd - bytes) >> 1 : charEnd - chars));
+
+ // Need new char* so we can check 4 at a time
+ ulong* longChars = (ulong*)chars;
+ ulong* longBytes = (ulong*)bytes;
+
+ while (longChars < longEnd)
+ {
+ // See if we potentially have surrogates (0x8000 bit set)
+ // (We're either big endian on a big endian machine or little endian on
+ // a little endian machine so this'll work)
+ if ((0x8000800080008000 & *longChars) != 0)
+ {
+ // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high
+ // 5 bits looks like 11011, then its a high or low surrogate.
+ // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
+ // Note that we expect BMP characters to be more common than surrogates
+ // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates
+ ulong uTemp = (0xf800f800f800f800 & *longChars) ^ 0xd800d800d800d800;
+
+ // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate
+ // but no clue if they're high or low.
+ // If each of the 4 characters are non-zero, then none are surrogates.
+ if ((uTemp & 0xFFFF000000000000) == 0 ||
+ (uTemp & 0x0000FFFF00000000) == 0 ||
+ (uTemp & 0x00000000FFFF0000) == 0 ||
+ (uTemp & 0x000000000000FFFF) == 0)
+ {
+ // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
+ // or if there's 1 or 4 surrogates
+
+ // If they happen to be high/low/high/low, we may as well continue. Check the next
+ // bit to see if its set (low) or not (high) in the right pattern
+#if BIGENDIAN
+ if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xd800dc00d800dc00) != 0)
+#else
+ if (((0xfc00fc00fc00fc00 & *longChars) ^ 0xdc00d800dc00d800) != 0)
+#endif
+ {
+ // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
+ // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
+
+ // Drop out to the slow loop to resolve the surrogates
+ break;
+ }
+ // else they are all surrogates in High/Low/High/Low order, so we can use them.
+ }
+ // else none are surrogates, so we can use them.
+ }
+ // else all < 0x8000 so we can use them
+
+ // We can use these 4 chars.
+ *longBytes = *longChars;
+ longChars++;
+ longBytes++;
+ }
+
+ chars = (char*)longChars;
+ bytes = (byte*)longBytes;
+
+ if (chars >= charEnd)
+ break;
+ }
+ // Not aligned, but maybe we can still be somewhat faster
+ // Also somehow this optimizes the above loop? It seems to cause something above
+ // to get enregistered, but I haven't figured out how to make that happen without this loop.
+ else if ((charLeftOver == 0) &&
+#if BIGENDIAN
+ bigEndian &&
+#else
+ !bigEndian &&
+#endif // BIGENDIAN
+
+#if BIT64
+ (unchecked((long)chars) & 7) != (unchecked((long)bytes) & 7) && // Only do this if chars & bytes are out of line, otherwise faster loop'll be faster next time
+#else
+ (unchecked((int)chars) & 3) != (unchecked((int)bytes) & 3) && // Only do this if chars & bytes are out of line, otherwise faster loop'll be faster next time
+#endif // BIT64
+ (unchecked((int)(bytes)) & 1) == 0)
+ {
+ // # to use
+ long iCount = ((byteEnd - bytes) >> 1 < charEnd - chars) ?
+ (byteEnd - bytes) >> 1 : charEnd - chars;
+
+ // Need new char*
+ char* charOut = ((char*)bytes); // a char* for our output
+ char* tempEnd = chars + iCount - 1; // Our end pointer
+
+ while (chars < tempEnd)
+ {
+ if (*chars >= (char)0xd800 && *chars <= (char)0xdfff)
+ {
+ // break for fallback for low surrogate
+ if (*chars >= 0xdc00)
+ break;
+
+ // break if next one's not a low surrogate (will do fallback)
+ if (*(chars + 1) < 0xdc00 || *(chars + 1) > 0xdfff)
+ break;
+
+ // They both exist, use them
+ }
+ // If 2nd char is surrogate & this one isn't then only add one
+ else if (*(chars + 1) >= (char)0xd800 && *(chars + 1) <= 0xdfff)
+ {
+ *charOut = *chars;
+ charOut++;
+ chars++;
+ continue;
+ }
+
+ *charOut = *chars;
+ *(charOut + 1) = *(chars + 1);
+ charOut += 2;
+ chars += 2;
+ }
+
+ bytes = (byte*)charOut;
+
+ if (chars >= charEnd)
+ break;
+ }
+#endif // !NO_FAST_UNICODE_LOOP
+
+ // No fallback, just get next char
+ ch = *chars;
+ chars++;
+ }
+
+ // Check for high or low surrogates
+ if (ch >= 0xd800 && ch <= 0xdfff)
+ {
+ // Was it a high surrogate?
+ if (ch <= 0xdbff)
+ {
+ // Its a high surrogate, see if we already had a high surrogate
+ if (charLeftOver > 0)
+ {
+ // Unwind the current character, this should be safe because we
+ // don't have leftover data in the fallback, so chars must have
+ // advanced already.
+ Debug.Assert(chars > charStart,
+ "[UnicodeEncoding.GetBytes]Expected chars to have advanced in unexpected high surrogate");
+ chars--;
+
+ // Fallback the previous surrogate
+ // Might need to create our fallback buffer
+ if (fallbackBuffer == null)
+ {
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
+ }
+
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
+ chars = charsForFallback;
+
+ charLeftOver = (char)0;
+ continue;
+ }
+
+ // Remember this high surrogate
+ charLeftOver = ch;
+ continue;
+ }
+
+ // Its a low surrogate
+ if (charLeftOver == 0)
+ {
+ // We'll fall back this one
+ // Might need to create our fallback buffer
+ if (fallbackBuffer == null)
+ {
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
+ }
+
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(ch, ref charsForFallback);
+ chars = charsForFallback;
+ continue;
+ }
+
+ // Valid surrogate pair, add our charLeftOver
+ if (bytes + 3 >= byteEnd)
+ {
+ // Not enough room to add this surrogate pair
+ if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
+ {
+ // These must have both been from the fallbacks.
+ // Both of these MUST have been from a fallback because if the 1st wasn't
+ // from a fallback, then a high surrogate followed by an illegal char
+ // would've caused the high surrogate to fall back. If a high surrogate
+ // fell back, then it was consumed and both chars came from the fallback.
+ fallbackBuffer.MovePrevious(); // Didn't use either fallback surrogate
+ fallbackBuffer.MovePrevious();
+ }
+ else
+ {
+ // If we don't have enough room, then either we should've advanced a while
+ // or we should have bytes==byteStart and throw below
+ Debug.Assert(chars > charStart + 1 || bytes == byteStart,
+ "[UnicodeEncoding.GetBytes]Expected chars to have when no room to add surrogate pair");
+ chars -= 2; // Didn't use either surrogate
+ }
+ ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written)
+ charLeftOver = (char)0; // we'll retry it later
+ break; // Didn't throw, but stop 'til next time.
+ }
+
+ if (bigEndian)
+ {
+ *(bytes++) = (byte)(charLeftOver >> 8);
+ *(bytes++) = (byte)charLeftOver;
+ }
+ else
+ {
+ *(bytes++) = (byte)charLeftOver;
+ *(bytes++) = (byte)(charLeftOver >> 8);
+ }
+
+ charLeftOver = (char)0;
+ }
+ else if (charLeftOver > 0)
+ {
+ // Expected a low surrogate, but this char is normal
+
+ // Rewind the current character, fallback previous character.
+ // this should be safe because we don't have leftover data in the
+ // fallback, so chars must have advanced already.
+ Debug.Assert(chars > charStart,
+ "[UnicodeEncoding.GetBytes]Expected chars to have advanced after expecting low surrogate");
+ chars--;
+
+ // fallback previous chars
+ // Might need to create our fallback buffer
+ if (fallbackBuffer == null)
+ {
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
+ }
+
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
+ chars = charsForFallback;
+
+ // Ignore charLeftOver or throw
+ charLeftOver = (char)0;
+ continue;
+ }
+
+ // Ok, we have a char to add
+ if (bytes + 1 >= byteEnd)
+ {
+ // Couldn't add this char
+ if (fallbackBuffer != null && fallbackBuffer.bFallingBack)
+ fallbackBuffer.MovePrevious(); // Not using this fallback char
+ else
+ {
+ // Lonely charLeftOver (from previous call) would've been caught up above,
+ // so this must be a case where we've already read an input char.
+ Debug.Assert(chars > charStart,
+ "[UnicodeEncoding.GetBytes]Expected chars to have advanced for failed fallback");
+ chars--; // Not using this char
+ }
+ ThrowBytesOverflow(encoder, bytes == byteStart); // Throw maybe (if no bytes written)
+ break; // didn't throw, just stop
+ }
+
+ if (bigEndian)
+ {
+ *(bytes++) = (byte)(ch >> 8);
+ *(bytes++) = (byte)ch;
+ }
+ else
+ {
+ *(bytes++) = (byte)ch;
+ *(bytes++) = (byte)(ch >> 8);
+ }
+ }
+
+ // Don't allocate space for left over char
+ if (charLeftOver > 0)
+ {
+ // If we aren't flushing we need to fall this back
+ if (encoder == null || encoder.MustFlush)
+ {
+ if (wasHereBefore)
+ {
+ // Throw it, using our complete character
+ throw new ArgumentException(
+ SR.Format(SR.Argument_RecursiveFallback, charLeftOver), nameof(chars));
+ }
+ else
+ {
+ // If we have to flush, stick it in fallback and try again
+ // Might need to create our fallback buffer
+ if (fallbackBuffer == null)
+ {
+ if (encoder == null)
+ fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = encoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(charStart, charEnd, encoder, true);
+ }
+
+ // If we're not flushing, this'll remember the left over character.
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ fallbackBuffer.InternalFallback(charLeftOver, ref charsForFallback);
+ chars = charsForFallback;
+
+ charLeftOver = (char)0;
+ wasHereBefore = true;
+ goto TryAgain;
+ }
+ }
+ }
+
+ // Not flushing, remember it in the encoder
+ if (encoder != null)
+ {
+ encoder.charLeftOver = charLeftOver;
+ encoder.m_charsUsed = (int)(chars - charStart);
+ }
+
+ // Remember charLeftOver if we must, or clear it if we're flushing
+ // (charLeftOver should be 0 if we're flushing)
+ Debug.Assert((encoder != null && !encoder.MustFlush) || charLeftOver == (char)0,
+ "[UnicodeEncoding.GetBytes] Expected no left over characters if flushing");
+
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0 ||
+ encoder == null || !encoder.m_throwOnOverflow,
+ "[UnicodeEncoding.GetBytes]Expected empty fallback buffer if not converting");
+
+ // We used to copy it fast, but this doesn't check for surrogates
+ // System.IO.__UnmanagedMemoryStream.memcpyimpl(bytes, (byte*)chars, usedByteCount);
+
+ return (int)(bytes - byteStart);
+ }
+
+ internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)
+ {
+ Debug.Assert(bytes != null, "[UnicodeEncoding.GetCharCount]bytes!=null");
+ Debug.Assert(count >= 0, "[UnicodeEncoding.GetCharCount]count >=0");
+
+ UnicodeEncoding.Decoder decoder = (UnicodeEncoding.Decoder)baseDecoder;
+
+ byte* byteEnd = bytes + count;
+ byte* byteStart = bytes;
+
+ // Need last vars
+ int lastByte = -1;
+ char lastChar = (char)0;
+
+ // Start by assuming same # of chars as bytes
+ int charCount = count >> 1;
+
+ // Need -1 to check 2 at a time. If we have an even #, longBytes will go
+ // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longBytes
+ // will go from longEnd - 1 long to longEnd. (Might not get to use this)
+ ulong* longEnd = (ulong*)(byteEnd - 7);
+
+ // For fallback we may need a fallback buffer
+ DecoderFallbackBuffer fallbackBuffer = null;
+
+ if (decoder != null)
+ {
+ lastByte = decoder.lastByte;
+ lastChar = decoder.lastChar;
+
+ // Assume extra char if last char was around
+ if (lastChar > 0)
+ charCount++;
+
+ // Assume extra char if extra last byte makes up odd # of input bytes
+ if (lastByte >= 0 && (count & 1) == 1)
+ {
+ charCount++;
+ }
+
+ // Shouldn't have anything in fallback buffer for GetCharCount
+ // (don't have to check m_throwOnOverflow for count)
+ Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
+ "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at start");
+ }
+
+ while (bytes < byteEnd)
+ {
+ // If we're aligned then maybe we can do it fast
+ // This'll hurt if we're unaligned because we'll always test but never be aligned
+#if !NO_FAST_UNICODE_LOOP
+#if BIGENDIAN
+ if (bigEndian &&
+#else // BIGENDIAN
+ if (!bigEndian &&
+#endif // BIGENDIAN
+#if BIT64 // win64 has to be long aligned
+ (unchecked((long)bytes) & 7) == 0 &&
+#else
+ (unchecked((int)bytes) & 3) == 0 &&
+#endif // BIT64
+ lastByte == -1 && lastChar == 0)
+ {
+ // Need new char* so we can check 4 at a time
+ ulong* longBytes = (ulong*)bytes;
+
+ while (longBytes < longEnd)
+ {
+ // See if we potentially have surrogates (0x8000 bit set)
+ // (We're either big endian on a big endian machine or little endian on
+ // a little endian machine so this'll work)
+ if ((0x8000800080008000 & *longBytes) != 0)
+ {
+ // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high
+ // 5 bits looks like 11011, then its a high or low surrogate.
+ // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
+ // Note that we expect BMP characters to be more common than surrogates
+ // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates
+ ulong uTemp = (0xf800f800f800f800 & *longBytes) ^ 0xd800d800d800d800;
+
+ // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate
+ // but no clue if they're high or low.
+ // If each of the 4 characters are non-zero, then none are surrogates.
+ if ((uTemp & 0xFFFF000000000000) == 0 ||
+ (uTemp & 0x0000FFFF00000000) == 0 ||
+ (uTemp & 0x00000000FFFF0000) == 0 ||
+ (uTemp & 0x000000000000FFFF) == 0)
+ {
+ // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
+ // or if there's 1 or 4 surrogates
+
+ // If they happen to be high/low/high/low, we may as well continue. Check the next
+ // bit to see if its set (low) or not (high) in the right pattern
+#if BIGENDIAN
+ if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xd800dc00d800dc00) != 0)
+#else
+ if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xdc00d800dc00d800) != 0)
+#endif
+ {
+ // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
+ // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
+
+ // Drop out to the slow loop to resolve the surrogates
+ break;
+ }
+ // else they are all surrogates in High/Low/High/Low order, so we can use them.
+ }
+ // else none are surrogates, so we can use them.
+ }
+ // else all < 0x8000 so we can use them
+
+ // We can use these 4 chars.
+ longBytes++;
+ }
+
+ bytes = (byte*)longBytes;
+
+ if (bytes >= byteEnd)
+ break;
+ }
+#endif // !NO_FAST_UNICODE_LOOP
+
+ // Get 1st byte
+ if (lastByte < 0)
+ {
+ lastByte = *bytes++;
+ if (bytes >= byteEnd) break;
+ }
+
+ // Get full char
+ char ch;
+ if (bigEndian)
+ {
+ ch = (char)(lastByte << 8 | *(bytes++));
+ }
+ else
+ {
+ ch = (char)(*(bytes++) << 8 | lastByte);
+ }
+ lastByte = -1;
+
+ // See if the char's valid
+ if (ch >= 0xd800 && ch <= 0xdfff)
+ {
+ // Was it a high surrogate?
+ if (ch <= 0xdbff)
+ {
+ // Its a high surrogate, if we had one then do fallback for previous one
+ if (lastChar > 0)
+ {
+ // Ignore previous bad high surrogate
+ charCount--;
+
+ // Get fallback for previous high surrogate
+ // Note we have to reconstruct bytes because some may have been in decoder
+ byte[] byteBuffer = null;
+ if (bigEndian)
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
+ }
+ else
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
+ }
+
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, null);
+ }
+
+ // Get fallback.
+ charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
+ }
+
+ // Ignore the last one which fell back already,
+ // and remember the new high surrogate
+ lastChar = ch;
+ continue;
+ }
+
+ // Its a low surrogate
+ if (lastChar == 0)
+ {
+ // Expected a previous high surrogate
+ charCount--;
+
+ // Get fallback for this low surrogate
+ // Note we have to reconstruct bytes because some may have been in decoder
+ byte[] byteBuffer = null;
+ if (bigEndian)
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)(ch >> 8)), unchecked((byte)ch) };
+ }
+ else
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)ch), unchecked((byte)(ch >> 8)) };
+ }
+
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, null);
+ }
+
+ charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
+
+ // Ignore this one (we already did its fallback)
+ continue;
+ }
+
+ // Valid surrogate pair, already counted.
+ lastChar = (char)0;
+ }
+ else if (lastChar > 0)
+ {
+ // Had a high surrogate, expected a low surrogate
+ // Uncount the last high surrogate
+ charCount--;
+
+ // fall back the high surrogate.
+ byte[] byteBuffer = null;
+ if (bigEndian)
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
+ }
+ else
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
+ }
+
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, null);
+ }
+
+ // Already subtracted high surrogate
+ charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
+
+ // Not left over now, clear previous high surrogate and continue to add current char
+ lastChar = (char)0;
+ }
+
+ // Valid char, already counted
+ }
+
+ // Extra space if we can't use decoder
+ if (decoder == null || decoder.MustFlush)
+ {
+ if (lastChar > 0)
+ {
+ // No hanging high surrogates allowed, do fallback and remove count for it
+ charCount--;
+ byte[] byteBuffer = null;
+ if (bigEndian)
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
+ }
+ else
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
+ }
+
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, null);
+ }
+
+ charCount += fallbackBuffer.InternalFallback(byteBuffer, bytes);
+
+ lastChar = (char)0;
+ }
+
+ if (lastByte >= 0)
+ {
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, null);
+ }
+
+ // No hanging odd bytes allowed if must flush
+ charCount += fallbackBuffer.InternalFallback(new byte[] { unchecked((byte)lastByte) }, bytes);
+ lastByte = -1;
+ }
+ }
+
+ // If we had a high surrogate left over, we can't count it
+ if (lastChar > 0)
+ charCount--;
+
+ // Shouldn't have anything in fallback buffer for GetCharCount
+ // (don't have to check m_throwOnOverflow for count)
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
+ "[UnicodeEncoding.GetCharCount]Expected empty fallback buffer at end");
+
+ return charCount;
+ }
+
+ internal override unsafe int GetChars(byte* bytes, int byteCount,
+ char* chars, int charCount, DecoderNLS baseDecoder)
+ {
+ Debug.Assert(chars != null, "[UnicodeEncoding.GetChars]chars!=null");
+ Debug.Assert(byteCount >= 0, "[UnicodeEncoding.GetChars]byteCount >=0");
+ Debug.Assert(charCount >= 0, "[UnicodeEncoding.GetChars]charCount >=0");
+ Debug.Assert(bytes != null, "[UnicodeEncoding.GetChars]bytes!=null");
+
+ UnicodeEncoding.Decoder decoder = (UnicodeEncoding.Decoder)baseDecoder;
+
+ // Need last vars
+ int lastByte = -1;
+ char lastChar = (char)0;
+
+ // Get our decoder (but don't clear it yet)
+ if (decoder != null)
+ {
+ lastByte = decoder.lastByte;
+ lastChar = decoder.lastChar;
+
+ // Shouldn't have anything in fallback buffer for GetChars
+ // (don't have to check m_throwOnOverflow for chars)
+ Debug.Assert(!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
+ "[UnicodeEncoding.GetChars]Expected empty fallback buffer at start");
+ }
+
+ // For fallback we may need a fallback buffer
+ DecoderFallbackBuffer fallbackBuffer = null;
+ char* charsForFallback;
+
+ byte* byteEnd = bytes + byteCount;
+ char* charEnd = chars + charCount;
+ byte* byteStart = bytes;
+ char* charStart = chars;
+
+ while (bytes < byteEnd)
+ {
+ // If we're aligned then maybe we can do it fast
+ // This'll hurt if we're unaligned because we'll always test but never be aligned
+#if !NO_FAST_UNICODE_LOOP
+#if BIGENDIAN
+ if (bigEndian &&
+#else // BIGENDIAN
+ if (!bigEndian &&
+#endif // BIGENDIAN
+#if BIT64 // win64 has to be long aligned
+ (unchecked((long)chars) & 7) == 0 && (unchecked((long)bytes) & 7) == 0 &&
+#else
+ (unchecked((int)chars) & 3) == 0 && (unchecked((int)bytes) & 3) == 0 &&
+#endif // BIT64
+ lastByte == -1 && lastChar == 0)
+ {
+ // Need -1 to check 2 at a time. If we have an even #, longChars will go
+ // from longEnd - 1/2 long to longEnd + 1/2 long. If we're odd, longChars
+ // will go from longEnd - 1 long to longEnd. (Might not get to use this)
+ // We can only go iCount units (limited by shorter of char or byte buffers.
+ ulong* longEnd = (ulong*)(bytes - 7 +
+ (((byteEnd - bytes) >> 1 < charEnd - chars) ?
+ (byteEnd - bytes) : (charEnd - chars) << 1));
+
+ // Need new char* so we can check 4 at a time
+ ulong* longBytes = (ulong*)bytes;
+ ulong* longChars = (ulong*)chars;
+
+ while (longBytes < longEnd)
+ {
+ // See if we potentially have surrogates (0x8000 bit set)
+ // (We're either big endian on a big endian machine or little endian on
+ // a little endian machine so this'll work)
+ if ((0x8000800080008000 & *longBytes) != 0)
+ {
+ // See if any of these are high or low surrogates (0xd800 - 0xdfff). If the high
+ // 5 bits looks like 11011, then its a high or low surrogate.
+ // We do the & f800 to filter the 5 bits, then ^ d800 to ensure the 0 isn't set.
+ // Note that we expect BMP characters to be more common than surrogates
+ // & each char with 11111... then ^ with 11011. Zeroes then indicate surrogates
+ ulong uTemp = (0xf800f800f800f800 & *longBytes) ^ 0xd800d800d800d800;
+
+ // Check each of the 4 chars. 0 for those 16 bits means it was a surrogate
+ // but no clue if they're high or low.
+ // If each of the 4 characters are non-zero, then none are surrogates.
+ if ((uTemp & 0xFFFF000000000000) == 0 ||
+ (uTemp & 0x0000FFFF00000000) == 0 ||
+ (uTemp & 0x00000000FFFF0000) == 0 ||
+ (uTemp & 0x000000000000FFFF) == 0)
+ {
+ // It has at least 1 surrogate, but we don't know if they're high or low surrogates,
+ // or if there's 1 or 4 surrogates
+
+ // If they happen to be high/low/high/low, we may as well continue. Check the next
+ // bit to see if its set (low) or not (high) in the right pattern
+#if BIGENDIAN
+ if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xd800dc00d800dc00) != 0)
+#else
+ if (((0xfc00fc00fc00fc00 & *longBytes) ^ 0xdc00d800dc00d800) != 0)
+#endif
+ {
+ // Either there weren't 4 surrogates, or the 0x0400 bit was set when a high
+ // was hoped for or the 0x0400 bit wasn't set where a low was hoped for.
+
+ // Drop out to the slow loop to resolve the surrogates
+ break;
+ }
+ // else they are all surrogates in High/Low/High/Low order, so we can use them.
+ }
+ // else none are surrogates, so we can use them.
+ }
+ // else all < 0x8000 so we can use them
+
+ // We can use these 4 chars.
+ *longChars = *longBytes;
+ longBytes++;
+ longChars++;
+ }
+
+ chars = (char*)longChars;
+ bytes = (byte*)longBytes;
+
+ if (bytes >= byteEnd)
+ break;
+ }
+#endif // !NO_FAST_UNICODE_LOOP
+
+ // Get 1st byte
+ if (lastByte < 0)
+ {
+ lastByte = *bytes++;
+ continue;
+ }
+
+ // Get full char
+ char ch;
+ if (bigEndian)
+ {
+ ch = (char)(lastByte << 8 | *(bytes++));
+ }
+ else
+ {
+ ch = (char)(*(bytes++) << 8 | lastByte);
+ }
+ lastByte = -1;
+
+ // See if the char's valid
+ if (ch >= 0xd800 && ch <= 0xdfff)
+ {
+ // Was it a high surrogate?
+ if (ch <= 0xdbff)
+ {
+ // Its a high surrogate, if we had one then do fallback for previous one
+ if (lastChar > 0)
+ {
+ // Get fallback for previous high surrogate
+ // Note we have to reconstruct bytes because some may have been in decoder
+ byte[] byteBuffer = null;
+ if (bigEndian)
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
+ }
+ else
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
+ }
+
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, charEnd);
+ }
+
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
+ chars = charsForFallback;
+
+ if (!fallbackResult)
+ {
+ // couldn't fall back lonely surrogate
+ // We either advanced bytes or chars should == charStart and throw below
+ Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
+ "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (bad surrogate)");
+ bytes -= 2; // didn't use these 2 bytes
+ fallbackBuffer.InternalReset();
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ break; // couldn't fallback but didn't throw
+ }
+ }
+
+ // Ignore the previous high surrogate which fell back already,
+ // yet remember the current high surrogate for next time.
+ lastChar = ch;
+ continue;
+ }
+
+ // Its a low surrogate
+ if (lastChar == 0)
+ {
+ // Expected a previous high surrogate
+ // Get fallback for this low surrogate
+ // Note we have to reconstruct bytes because some may have been in decoder
+ byte[] byteBuffer = null;
+ if (bigEndian)
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)(ch >> 8)), unchecked((byte)ch) };
+ }
+ else
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)ch), unchecked((byte)(ch >> 8)) };
+ }
+
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, charEnd);
+ }
+
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
+ chars = charsForFallback;
+
+ if (!fallbackResult)
+ {
+ // couldn't fall back lonely surrogate
+ // We either advanced bytes or chars should == charStart and throw below
+ Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
+ "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (lonely surrogate)");
+ bytes -= 2; // didn't use these 2 bytes
+ fallbackBuffer.InternalReset();
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ break; // couldn't fallback but didn't throw
+ }
+
+ // Didn't throw, ignore this one (we already did its fallback)
+ continue;
+ }
+
+ // Valid surrogate pair, add our lastChar (will need 2 chars)
+ if (chars >= charEnd - 1)
+ {
+ // couldn't find room for this surrogate pair
+ // We either advanced bytes or chars should == charStart and throw below
+ Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
+ "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (surrogate pair)");
+ bytes -= 2; // didn't use these 2 bytes
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ // Leave lastChar for next call to Convert()
+ break; // couldn't fallback but didn't throw
+ }
+
+ *chars++ = lastChar;
+ lastChar = (char)0;
+ }
+ else if (lastChar > 0)
+ {
+ // Had a high surrogate, expected a low surrogate, fall back the high surrogate.
+ byte[] byteBuffer = null;
+ if (bigEndian)
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
+ }
+ else
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
+ }
+
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, charEnd);
+ }
+
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
+ chars = charsForFallback;
+
+ if (!fallbackResult)
+ {
+ // couldn't fall back high surrogate, or char that would be next
+ // We either advanced bytes or chars should == charStart and throw below
+ Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
+ "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (no low surrogate)");
+ bytes -= 2; // didn't use these 2 bytes
+ fallbackBuffer.InternalReset();
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ break; // couldn't fallback but didn't throw
+ }
+
+ // Not left over now, clear previous high surrogate and continue to add current char
+ lastChar = (char)0;
+ }
+
+ // Valid char, room for it?
+ if (chars >= charEnd)
+ {
+ // 2 bytes couldn't fall back
+ // We either advanced bytes or chars should == charStart and throw below
+ Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
+ "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (normal)");
+ bytes -= 2; // didn't use these bytes
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ break; // couldn't fallback but didn't throw
+ }
+
+ // add it
+ *chars++ = ch;
+ }
+
+ // Remember our decoder if we must
+ if (decoder == null || decoder.MustFlush)
+ {
+ if (lastChar > 0)
+ {
+ // No hanging high surrogates allowed, do fallback and remove count for it
+ byte[] byteBuffer = null;
+ if (bigEndian)
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)(lastChar >> 8)), unchecked((byte)lastChar) };
+ }
+ else
+ {
+ byteBuffer = new byte[]
+ { unchecked((byte)lastChar), unchecked((byte)(lastChar >> 8)) };
+ }
+
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, charEnd);
+ }
+
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ bool fallbackResult = fallbackBuffer.InternalFallback(byteBuffer, bytes, ref charsForFallback);
+ chars = charsForFallback;
+
+ if (!fallbackResult)
+ {
+ // 2 bytes couldn't fall back
+ // We either advanced bytes or chars should == charStart and throw below
+ Debug.Assert(bytes >= byteStart + 2 || chars == charStart,
+ "[UnicodeEncoding.GetChars]Expected bytes to have advanced or no output (decoder)");
+ bytes -= 2; // didn't use these bytes
+ if (lastByte >= 0)
+ bytes--; // had an extra last byte hanging around
+ fallbackBuffer.InternalReset();
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ // We'll remember these in our decoder though
+ bytes += 2;
+ if (lastByte >= 0)
+ bytes++;
+ goto End;
+ }
+
+ // done with this one
+ lastChar = (char)0;
+ }
+
+ if (lastByte >= 0)
+ {
+ if (fallbackBuffer == null)
+ {
+ if (decoder == null)
+ fallbackBuffer = this.decoderFallback.CreateFallbackBuffer();
+ else
+ fallbackBuffer = decoder.FallbackBuffer;
+
+ // Set our internal fallback interesting things.
+ fallbackBuffer.InternalInitialize(byteStart, charEnd);
+ }
+
+ // No hanging odd bytes allowed if must flush
+ charsForFallback = chars; // Avoid passing chars by reference to allow it to be enregistered
+ bool fallbackResult = fallbackBuffer.InternalFallback(new byte[] { unchecked((byte)lastByte) }, bytes, ref charsForFallback);
+ chars = charsForFallback;
+
+ if (!fallbackResult)
+ {
+ // odd byte couldn't fall back
+ bytes--; // didn't use this byte
+ fallbackBuffer.InternalReset();
+ ThrowCharsOverflow(decoder, chars == charStart);// Might throw, if no chars output
+ // didn't throw, but we'll remember it in the decoder
+ bytes++;
+ goto End;
+ }
+
+ // Didn't fail, clear buffer
+ lastByte = -1;
+ }
+ }
+
+ End:
+
+ // Remember our decoder if we must
+ if (decoder != null)
+ {
+ Debug.Assert((decoder.MustFlush == false) || ((lastChar == (char)0) && (lastByte == -1)),
+ "[UnicodeEncoding.GetChars] Expected no left over chars or bytes if flushing"
+ // + " " + ((int)lastChar).ToString("X4") + " " + lastByte.ToString("X2")
+ );
+
+ decoder.m_bytesUsed = (int)(bytes - byteStart);
+ decoder.lastChar = lastChar;
+ decoder.lastByte = lastByte;
+ }
+
+ // Used to do this the old way
+ // System.IO.__UnmanagedMemoryStream.memcpyimpl((byte*)chars, bytes, byteCount);
+
+ // Shouldn't have anything in fallback buffer for GetChars
+ // (don't have to check m_throwOnOverflow for count or chars)
+ Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
+ "[UnicodeEncoding.GetChars]Expected empty fallback buffer at end");
+
+ return (int)(chars - charStart);
+ }
+
+
+ public override System.Text.Encoder GetEncoder()
+ {
+ return new EncoderNLS(this);
+ }
+
+
+ public override System.Text.Decoder GetDecoder()
+ {
+ return new UnicodeEncoding.Decoder(this);
+ }
+
+
+ public override byte[] GetPreamble()
+ {
+ if (byteOrderMark)
+ {
+ // Note - we must allocate new byte[]'s here to prevent someone
+ // from modifying a cached byte[].
+ if (bigEndian)
+ return new byte[2] { 0xfe, 0xff };
+ else
+ return new byte[2] { 0xff, 0xfe };
+ }
+ return Array.Empty<Byte>();
+ }
+
+
+ public override int GetMaxByteCount(int charCount)
+ {
+ if (charCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(charCount),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // Characters would be # of characters + 1 in case left over high surrogate is ? * max fallback
+ long byteCount = (long)charCount + 1;
+
+ if (EncoderFallback.MaxCharCount > 1)
+ byteCount *= EncoderFallback.MaxCharCount;
+
+ // 2 bytes per char
+ byteCount <<= 1;
+
+ if (byteCount > 0x7fffffff)
+ throw new ArgumentOutOfRangeException(nameof(charCount), SR.ArgumentOutOfRange_GetByteCountOverflow);
+
+ return (int)byteCount;
+ }
+
+
+ public override int GetMaxCharCount(int byteCount)
+ {
+ if (byteCount < 0)
+ throw new ArgumentOutOfRangeException(nameof(byteCount),
+ SR.ArgumentOutOfRange_NeedNonNegNum);
+ Contract.EndContractBlock();
+
+ // long because byteCount could be biggest int.
+ // 1 char per 2 bytes. Round up in case 1 left over in decoder.
+ // Round up using &1 in case byteCount is max size
+ // Might also need an extra 1 if there's a left over high surrogate in the decoder.
+ long charCount = (long)(byteCount >> 1) + (byteCount & 1) + 1;
+
+ // Don't forget fallback (in case they have a bunch of lonely surrogates or something bizzare like that)
+ if (DecoderFallback.MaxCharCount > 1)
+ charCount *= DecoderFallback.MaxCharCount;
+
+ if (charCount > 0x7fffffff)
+ throw new ArgumentOutOfRangeException(nameof(byteCount), SR.ArgumentOutOfRange_GetCharCountOverflow);
+
+ return (int)charCount;
+ }
+
+
+ public override bool Equals(Object value)
+ {
+ UnicodeEncoding that = value as UnicodeEncoding;
+ if (that != null)
+ {
+ //
+ // Big Endian Unicode has different code page (1201) than small Endian one (1200),
+ // so we still have to check m_codePage here.
+ //
+ return (CodePage == that.CodePage) &&
+ byteOrderMark == that.byteOrderMark &&
+ // isThrowException == that.isThrowException && // Same as Encoder/Decoder being exception fallbacks
+ bigEndian == that.bigEndian &&
+ (EncoderFallback.Equals(that.EncoderFallback)) &&
+ (DecoderFallback.Equals(that.DecoderFallback));
+ }
+ return (false);
+ }
+
+ public override int GetHashCode()
+ {
+ return CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode() +
+ (byteOrderMark ? 4 : 0) + (bigEndian ? 8 : 0);
+ }
+
+ [Serializable]
+ private sealed class Decoder : System.Text.DecoderNLS, ISerializable
+ {
+ internal int lastByte = -1;
+ internal char lastChar = '\0';
+
+ public Decoder(UnicodeEncoding encoding) : base(encoding)
+ {
+ // base calls reset
+ }
+
+ // Constructor called by serialization, have to handle deserializing from Everett
+ internal Decoder(SerializationInfo info, StreamingContext context)
+ {
+ // Any info?
+ if (info == null) throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ // Get Common Info
+ this.lastByte = (int)info.GetValue("lastByte", typeof(int));
+
+ try
+ {
+ // Try the encoding, which is only serialized in Whidbey
+ this.m_encoding = (Encoding)info.GetValue("m_encoding", typeof(Encoding));
+ this.lastChar = (char)info.GetValue("lastChar", typeof(char));
+ this.m_fallback = (DecoderFallback)info.GetValue("m_fallback", typeof(DecoderFallback));
+ }
+ catch (SerializationException)
+ {
+ // Everett didn't serialize the UnicodeEncoding, get the default one
+ bool bigEndian = (bool)info.GetValue("bigEndian", typeof(bool));
+ this.m_encoding = new UnicodeEncoding(bigEndian, false);
+ }
+ }
+
+ // ISerializable implementation, get data for this object
+ void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ // Any info?
+ if (info == null) throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ // Save Whidbey data
+ info.AddValue("m_encoding", this.m_encoding);
+ info.AddValue("m_fallback", this.m_fallback);
+ info.AddValue("lastChar", this.lastChar); // Unused by everett so it'll probably get lost
+ info.AddValue("lastByte", this.lastByte);
+
+ // Everett Only
+ info.AddValue("bigEndian", ((UnicodeEncoding)(this.m_encoding)).bigEndian);
+ }
+
+ public override void Reset()
+ {
+ lastByte = -1;
+ lastChar = '\0';
+ if (m_fallbackBuffer != null)
+ m_fallbackBuffer.Reset();
+ }
+
+ // Anything left in our decoder?
+ internal override bool HasState
+ {
+ get
+ {
+ return (this.lastByte != -1 || this.lastChar != '\0');
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/ThreadAttributes.cs b/src/mscorlib/shared/System/ThreadAttributes.cs
new file mode 100644
index 0000000000..6248736107
--- /dev/null
+++ b/src/mscorlib/shared/System/ThreadAttributes.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Purpose: For Threads-related custom attributes.
+**
+=============================================================================*/
+
+namespace System
+{
+ [AttributeUsage(AttributeTargets.Method)]
+ public sealed class STAThreadAttribute : Attribute
+ {
+ public STAThreadAttribute()
+ {
+ }
+ }
+
+ [AttributeUsage(AttributeTargets.Method)]
+ public sealed class MTAThreadAttribute : Attribute
+ {
+ public MTAThreadAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/ThreadStaticAttribute.cs b/src/mscorlib/shared/System/ThreadStaticAttribute.cs
new file mode 100644
index 0000000000..3755e65a7b
--- /dev/null
+++ b/src/mscorlib/shared/System/ThreadStaticAttribute.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Custom attribute to indicate that the field should be treated
+** as a static relative to a thread.
+**
+**
+**
+===========================================================*/
+
+using System;
+
+namespace System
+{
+ [Serializable]
+ [AttributeUsage(AttributeTargets.Field, Inherited = false)]
+ public class ThreadStaticAttribute : Attribute
+ {
+ public ThreadStaticAttribute()
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs b/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs
new file mode 100644
index 0000000000..8056a3b330
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/AbandonedMutexException.cs
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+// AbandonedMutexException
+// Thrown when a wait completes because one or more mutexes was abandoned.
+// AbandonedMutexs indicate serious error in user code or machine state.
+////////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Threading;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class AbandonedMutexException : SystemException
+ {
+ private int _mutexIndex = -1;
+ private Mutex _mutex = null;
+
+ public AbandonedMutexException()
+ : base(SR.Threading_AbandonedMutexException)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ }
+
+ public AbandonedMutexException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ }
+
+ public AbandonedMutexException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ }
+
+ public AbandonedMutexException(int location, WaitHandle handle)
+ : base(SR.Threading_AbandonedMutexException)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ SetupException(location, handle);
+ }
+
+ public AbandonedMutexException(String message, int location, WaitHandle handle)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ SetupException(location, handle);
+ }
+
+ public AbandonedMutexException(String message, Exception inner, int location, WaitHandle handle)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_ABANDONEDMUTEX;
+ SetupException(location, handle);
+ }
+
+ protected AbandonedMutexException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ private void SetupException(int location, WaitHandle handle)
+ {
+ _mutexIndex = location;
+ if (handle != null)
+ _mutex = handle as Mutex;
+ }
+
+ public Mutex Mutex => _mutex;
+ public int MutexIndex => _mutexIndex;
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ApartmentState.cs b/src/mscorlib/shared/System/Threading/ApartmentState.cs
new file mode 100644
index 0000000000..47c1677cb5
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ApartmentState.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading
+{
+ public enum ApartmentState
+ {
+ /*=========================================================================
+ ** Constants for thread apartment states.
+ =========================================================================*/
+ STA = 0,
+ MTA = 1,
+ Unknown = 2
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/AsyncLocal.cs b/src/mscorlib/shared/System/Threading/AsyncLocal.cs
new file mode 100644
index 0000000000..59c8fb3c88
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/AsyncLocal.cs
@@ -0,0 +1,484 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections.Generic;
+using System.Diagnostics;
+
+namespace System.Threading
+{
+ //
+ // AsyncLocal<T> represents "ambient" data that is local to a given asynchronous control flow, such as an
+ // async method. For example, say you want to associate a culture with a given async flow:
+ //
+ // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>();
+ //
+ // static async Task SomeOperationAsync(Culture culture)
+ // {
+ // s_currentCulture.Value = culture;
+ //
+ // await FooAsync();
+ // }
+ //
+ // static async Task FooAsync()
+ // {
+ // PrintStringWithCulture(s_currentCulture.Value);
+ // }
+ //
+ // AsyncLocal<T> also provides optional notifications when the value associated with the current thread
+ // changes, either because it was explicitly changed by setting the Value property, or implicitly changed
+ // when the thread encountered an "await" or other context transition. For example, we might want our
+ // current culture to be communicated to the OS as well:
+ //
+ // static AsyncLocal<Culture> s_currentCulture = new AsyncLocal<Culture>(
+ // args =>
+ // {
+ // NativeMethods.SetThreadCulture(args.CurrentValue.LCID);
+ // });
+ //
+ public sealed class AsyncLocal<T> : IAsyncLocal
+ {
+ private readonly Action<AsyncLocalValueChangedArgs<T>> m_valueChangedHandler;
+
+ //
+ // Constructs an AsyncLocal<T> that does not receive change notifications.
+ //
+ public AsyncLocal()
+ {
+ }
+
+ //
+ // Constructs an AsyncLocal<T> with a delegate that is called whenever the current value changes
+ // on any thread.
+ //
+ public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>> valueChangedHandler)
+ {
+ m_valueChangedHandler = valueChangedHandler;
+ }
+
+ public T Value
+ {
+ get
+ {
+ object obj = ExecutionContext.GetLocalValue(this);
+ return (obj == null) ? default(T) : (T)obj;
+ }
+ set
+ {
+ ExecutionContext.SetLocalValue(this, value, m_valueChangedHandler != null);
+ }
+ }
+
+ void IAsyncLocal.OnValueChanged(object previousValueObj, object currentValueObj, bool contextChanged)
+ {
+ Debug.Assert(m_valueChangedHandler != null);
+ T previousValue = previousValueObj == null ? default(T) : (T)previousValueObj;
+ T currentValue = currentValueObj == null ? default(T) : (T)currentValueObj;
+ m_valueChangedHandler(new AsyncLocalValueChangedArgs<T>(previousValue, currentValue, contextChanged));
+ }
+ }
+
+ //
+ // Interface to allow non-generic code in ExecutionContext to call into the generic AsyncLocal<T> type.
+ //
+ internal interface IAsyncLocal
+ {
+ void OnValueChanged(object previousValue, object currentValue, bool contextChanged);
+ }
+
+ public struct AsyncLocalValueChangedArgs<T>
+ {
+ public T PreviousValue { get; private set; }
+ public T CurrentValue { get; private set; }
+
+ //
+ // If the value changed because we changed to a different ExecutionContext, this is true. If it changed
+ // because someone set the Value property, this is false.
+ //
+ public bool ThreadContextChanged { get; private set; }
+
+ internal AsyncLocalValueChangedArgs(T previousValue, T currentValue, bool contextChanged)
+ : this()
+ {
+ PreviousValue = previousValue;
+ CurrentValue = currentValue;
+ ThreadContextChanged = contextChanged;
+ }
+ }
+
+ //
+ // Interface used to store an IAsyncLocal => object mapping in ExecutionContext.
+ // Implementations are specialized based on the number of elements in the immutable
+ // map in order to minimize memory consumption and look-up times.
+ //
+ internal interface IAsyncLocalValueMap
+ {
+ bool TryGetValue(IAsyncLocal key, out object value);
+ IAsyncLocalValueMap Set(IAsyncLocal key, object value);
+ }
+
+ //
+ // Utility functions for getting/creating instances of IAsyncLocalValueMap
+ //
+ internal static class AsyncLocalValueMap
+ {
+ public static IAsyncLocalValueMap Empty { get; } = new EmptyAsyncLocalValueMap();
+
+ // Instance without any key/value pairs. Used as a singleton/
+ private sealed class EmptyAsyncLocalValueMap : IAsyncLocalValueMap
+ {
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ {
+ // If the value isn't null, then create a new one-element map to store
+ // the key/value pair. If it is null, then we're still empty.
+ return value != null ?
+ new OneElementAsyncLocalValueMap(key, value) :
+ (IAsyncLocalValueMap)this;
+ }
+
+ public bool TryGetValue(IAsyncLocal key, out object value)
+ {
+ value = null;
+ return false;
+ }
+ }
+
+ // Instance with one key/value pair.
+ private sealed class OneElementAsyncLocalValueMap : IAsyncLocalValueMap
+ {
+ private readonly IAsyncLocal _key1;
+ private readonly object _value1;
+
+ public OneElementAsyncLocalValueMap(IAsyncLocal key, object value)
+ {
+ _key1 = key; _value1 = value;
+ }
+
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ {
+ if (value != null)
+ {
+ // The value is non-null. If the key matches one already contained in this map,
+ // then create a new one-element map with the updated value, otherwise create
+ // a two-element map with the additional key/value.
+ return ReferenceEquals(key, _key1) ?
+ new OneElementAsyncLocalValueMap(key, value) :
+ (IAsyncLocalValueMap)new TwoElementAsyncLocalValueMap(_key1, _value1, key, value);
+ }
+ else
+ {
+ // The value is null. If the key exists in this map, remove it by downgrading to an empty map.
+ // Otherwise, there's nothing to add or remove, so just return this map.
+ return ReferenceEquals(key, _key1) ?
+ Empty :
+ (IAsyncLocalValueMap)this;
+ }
+ }
+
+ public bool TryGetValue(IAsyncLocal key, out object value)
+ {
+ if (ReferenceEquals(key, _key1))
+ {
+ value = _value1;
+ return true;
+ }
+ else
+ {
+ value = null;
+ return false;
+ }
+ }
+ }
+
+ // Instance with two key/value pairs.
+ private sealed class TwoElementAsyncLocalValueMap : IAsyncLocalValueMap
+ {
+ private readonly IAsyncLocal _key1, _key2;
+ private readonly object _value1, _value2;
+
+ public TwoElementAsyncLocalValueMap(IAsyncLocal key1, object value1, IAsyncLocal key2, object value2)
+ {
+ _key1 = key1; _value1 = value1;
+ _key2 = key2; _value2 = value2;
+ }
+
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ {
+ if (value != null)
+ {
+ // The value is non-null. If the key matches one already contained in this map,
+ // then create a new two-element map with the updated value, otherwise create
+ // a three-element map with the additional key/value.
+ return
+ ReferenceEquals(key, _key1) ? new TwoElementAsyncLocalValueMap(key, value, _key2, _value2) :
+ ReferenceEquals(key, _key2) ? new TwoElementAsyncLocalValueMap(_key1, _value1, key, value) :
+ (IAsyncLocalValueMap)new ThreeElementAsyncLocalValueMap(_key1, _value1, _key2, _value2, key, value);
+ }
+ else
+ {
+ // The value is null. If the key exists in this map, remove it by downgrading to a one-element map
+ // without the key. Otherwise, there's nothing to add or remove, so just return this map.
+ return
+ ReferenceEquals(key, _key1) ? new OneElementAsyncLocalValueMap(_key2, _value2) :
+ ReferenceEquals(key, _key2) ? new OneElementAsyncLocalValueMap(_key1, _value1) :
+ (IAsyncLocalValueMap)this;
+ }
+ }
+
+ public bool TryGetValue(IAsyncLocal key, out object value)
+ {
+ if (ReferenceEquals(key, _key1))
+ {
+ value = _value1;
+ return true;
+ }
+ else if (ReferenceEquals(key, _key2))
+ {
+ value = _value2;
+ return true;
+ }
+ else
+ {
+ value = null;
+ return false;
+ }
+ }
+ }
+
+ // Instance with three key/value pairs.
+ private sealed class ThreeElementAsyncLocalValueMap : IAsyncLocalValueMap
+ {
+ private readonly IAsyncLocal _key1, _key2, _key3;
+ private readonly object _value1, _value2, _value3;
+
+ public ThreeElementAsyncLocalValueMap(IAsyncLocal key1, object value1, IAsyncLocal key2, object value2, IAsyncLocal key3, object value3)
+ {
+ _key1 = key1; _value1 = value1;
+ _key2 = key2; _value2 = value2;
+ _key3 = key3; _value3 = value3;
+ }
+
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ {
+ if (value != null)
+ {
+ // The value is non-null. If the key matches one already contained in this map,
+ // then create a new three-element map with the updated value.
+ if (ReferenceEquals(key, _key1)) return new ThreeElementAsyncLocalValueMap(key, value, _key2, _value2, _key3, _value3);
+ if (ReferenceEquals(key, _key2)) return new ThreeElementAsyncLocalValueMap(_key1, _value1, key, value, _key3, _value3);
+ if (ReferenceEquals(key, _key3)) return new ThreeElementAsyncLocalValueMap(_key1, _value1, _key2, _value2, key, value);
+
+ // The key doesn't exist in this map, so upgrade to a multi map that contains
+ // the additional key/value pair.
+ var multi = new MultiElementAsyncLocalValueMap(4);
+ multi.UnsafeStore(0, _key1, _value1);
+ multi.UnsafeStore(1, _key2, _value2);
+ multi.UnsafeStore(2, _key3, _value3);
+ multi.UnsafeStore(3, key, value);
+ return multi;
+ }
+ else
+ {
+ // The value is null. If the key exists in this map, remove it by downgrading to a two-element map
+ // without the key. Otherwise, there's nothing to add or remove, so just return this map.
+ return
+ ReferenceEquals(key, _key1) ? new TwoElementAsyncLocalValueMap(_key2, _value2, _key3, _value3) :
+ ReferenceEquals(key, _key2) ? new TwoElementAsyncLocalValueMap(_key1, _value1, _key3, _value3) :
+ ReferenceEquals(key, _key3) ? new TwoElementAsyncLocalValueMap(_key1, _value1, _key2, _value2) :
+ (IAsyncLocalValueMap)this;
+ }
+ }
+
+ public bool TryGetValue(IAsyncLocal key, out object value)
+ {
+ if (ReferenceEquals(key, _key1))
+ {
+ value = _value1;
+ return true;
+ }
+ else if (ReferenceEquals(key, _key2))
+ {
+ value = _value2;
+ return true;
+ }
+ else if (ReferenceEquals(key, _key3))
+ {
+ value = _value3;
+ return true;
+ }
+ else
+ {
+ value = null;
+ return false;
+ }
+ }
+ }
+
+ // Instance with up to 16 key/value pairs.
+ private sealed class MultiElementAsyncLocalValueMap : IAsyncLocalValueMap
+ {
+ internal const int MaxMultiElements = 16;
+ private readonly KeyValuePair<IAsyncLocal, object>[] _keyValues;
+
+ internal MultiElementAsyncLocalValueMap(int count)
+ {
+ Debug.Assert(count <= MaxMultiElements);
+ _keyValues = new KeyValuePair<IAsyncLocal, object>[count];
+ }
+
+ internal void UnsafeStore(int index, IAsyncLocal key, object value)
+ {
+ Debug.Assert(index < _keyValues.Length);
+ _keyValues[index] = new KeyValuePair<IAsyncLocal, object>(key, value);
+ }
+
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ {
+ // Find the key in this map.
+ for (int i = 0; i < _keyValues.Length; i++)
+ {
+ if (ReferenceEquals(key, _keyValues[i].Key))
+ {
+ // The key is in the map. If the value isn't null, then create a new map of the same
+ // size that has all of the same pairs, with this new key/value pair overwriting the old.
+ if (value != null)
+ {
+ var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length);
+ Array.Copy(_keyValues, 0, multi._keyValues, 0, _keyValues.Length);
+ multi._keyValues[i] = new KeyValuePair<IAsyncLocal, object>(key, value);
+ return multi;
+ }
+ else if (_keyValues.Length == 4)
+ {
+ // The value is null, and we only have four elements, one of which we're removing,
+ // so downgrade to a three-element map, without the matching element.
+ return
+ i == 0 ? new ThreeElementAsyncLocalValueMap(_keyValues[1].Key, _keyValues[1].Value, _keyValues[2].Key, _keyValues[2].Value, _keyValues[3].Key, _keyValues[3].Value) :
+ i == 1 ? new ThreeElementAsyncLocalValueMap(_keyValues[0].Key, _keyValues[0].Value, _keyValues[2].Key, _keyValues[2].Value, _keyValues[3].Key, _keyValues[3].Value) :
+ i == 2 ? new ThreeElementAsyncLocalValueMap(_keyValues[0].Key, _keyValues[0].Value, _keyValues[1].Key, _keyValues[1].Value, _keyValues[3].Key, _keyValues[3].Value) :
+ (IAsyncLocalValueMap)new ThreeElementAsyncLocalValueMap(_keyValues[0].Key, _keyValues[0].Value, _keyValues[1].Key, _keyValues[1].Value, _keyValues[2].Key, _keyValues[2].Value);
+ }
+ else
+ {
+ // The value is null, and we have enough elements remaining to warrant a multi map.
+ // Create a new one and copy all of the elements from this one, except the one to be removed.
+ var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length - 1);
+ if (i != 0) Array.Copy(_keyValues, 0, multi._keyValues, 0, i);
+ if (i != _keyValues.Length - 1) Array.Copy(_keyValues, i + 1, multi._keyValues, i, _keyValues.Length - i - 1);
+ return multi;
+ }
+ }
+ }
+
+ // The key does not already exist in this map.
+
+ // If the value is null, then we can simply return this same map, as there's nothing to add or remove.
+ if (value == null)
+ {
+ return this;
+ }
+
+ // We need to create a new map that has the additional key/value pair.
+ // If with the addition we can still fit in a multi map, create one.
+ if (_keyValues.Length < MaxMultiElements)
+ {
+ var multi = new MultiElementAsyncLocalValueMap(_keyValues.Length + 1);
+ Array.Copy(_keyValues, 0, multi._keyValues, 0, _keyValues.Length);
+ multi._keyValues[_keyValues.Length] = new KeyValuePair<IAsyncLocal, object>(key, value);
+ return multi;
+ }
+
+ // Otherwise, upgrade to a many map.
+ var many = new ManyElementAsyncLocalValueMap(MaxMultiElements + 1);
+ foreach (KeyValuePair<IAsyncLocal, object> pair in _keyValues)
+ {
+ many[pair.Key] = pair.Value;
+ }
+ many[key] = value;
+ return many;
+ }
+
+ public bool TryGetValue(IAsyncLocal key, out object value)
+ {
+ foreach (KeyValuePair<IAsyncLocal, object> pair in _keyValues)
+ {
+ if (ReferenceEquals(key, pair.Key))
+ {
+ value = pair.Value;
+ return true;
+ }
+ }
+ value = null;
+ return false;
+ }
+ }
+
+ // Instance with any number of key/value pairs.
+ private sealed class ManyElementAsyncLocalValueMap : Dictionary<IAsyncLocal, object>, IAsyncLocalValueMap
+ {
+ public ManyElementAsyncLocalValueMap(int capacity) : base(capacity) { }
+
+ public IAsyncLocalValueMap Set(IAsyncLocal key, object value)
+ {
+ int count = Count;
+ bool containsKey = ContainsKey(key);
+
+ // If the value being set exists, create a new many map, copy all of the elements from this one,
+ // and then store the new key/value pair into it. This is the most common case.
+ if (value != null)
+ {
+ var map = new ManyElementAsyncLocalValueMap(count + (containsKey ? 0 : 1));
+ foreach (KeyValuePair<IAsyncLocal, object> pair in this)
+ {
+ map[pair.Key] = pair.Value;
+ }
+ map[key] = value;
+ return map;
+ }
+
+ // Otherwise, the value is null, which means null is being stored into an AsyncLocal.Value.
+ // Since there's no observable difference at the API level between storing null and the key
+ // not existing at all, we can downgrade to a smaller map rather than storing null.
+
+ // If the key is contained in this map, we're going to create a new map that's one pair smaller.
+ if (containsKey)
+ {
+ // If the new count would be within range of a multi map instead of a many map,
+ // downgrade to the many map, which uses less memory and is faster to access.
+ // Otherwise, just create a new many map that's missing this key.
+ if (count == MultiElementAsyncLocalValueMap.MaxMultiElements + 1)
+ {
+ var multi = new MultiElementAsyncLocalValueMap(MultiElementAsyncLocalValueMap.MaxMultiElements);
+ int index = 0;
+ foreach (KeyValuePair<IAsyncLocal, object> pair in this)
+ {
+ if (!ReferenceEquals(key, pair.Key))
+ {
+ multi.UnsafeStore(index++, pair.Key, pair.Value);
+ }
+ }
+ Debug.Assert(index == MultiElementAsyncLocalValueMap.MaxMultiElements);
+ return multi;
+ }
+ else
+ {
+ var map = new ManyElementAsyncLocalValueMap(count - 1);
+ foreach (KeyValuePair<IAsyncLocal, object> pair in this)
+ {
+ if (!ReferenceEquals(key, pair.Key))
+ {
+ map[pair.Key] = pair.Value;
+ }
+ }
+ Debug.Assert(map.Count == count - 1);
+ return map;
+ }
+ }
+
+ // We were storing null, but the key wasn't in the map, so there's nothing to change.
+ // Just return this instance.
+ return this;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/AutoResetEvent.cs b/src/mscorlib/shared/System/Threading/AutoResetEvent.cs
new file mode 100644
index 0000000000..8320d7ad5a
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/AutoResetEvent.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading
+{
+ public sealed class AutoResetEvent : EventWaitHandle
+ {
+ public AutoResetEvent(bool initialState) : base(initialState, EventResetMode.AutoReset) { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs b/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs
new file mode 100644
index 0000000000..89380fee60
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/DeferredDisposableLifetime.cs
@@ -0,0 +1,116 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+
+namespace System.Threading
+{
+ /// <summary>
+ /// Provides callbacks to objects whose lifetime is managed by <see cref="DeferredDisposableLifetime{T}"/>.
+ /// </summary>
+ internal interface IDeferredDisposable
+ {
+ /// <summary>
+ /// Called when the object's refcount reaches zero.
+ /// </summary>
+ /// <param name="disposed">
+ /// Indicates whether the object has been disposed.
+ /// </param>
+ /// <remarks>
+ /// If the refount reaches zero before the object is disposed, this method will be called with
+ /// <paramref name="disposed"/> set to false. If the object is then disposed, this method will be
+ /// called again, with <paramref name="disposed"/> set to true. If the refcount reaches zero
+ /// after the object has already been disposed, this will be called a single time, with
+ /// <paramref name="disposed"/> set to true.
+ /// </remarks>
+ void OnFinalRelease(bool disposed);
+ }
+
+ /// <summary>
+ /// Manages the lifetime of an object which implements IDisposable, but which must defer the actual
+ /// cleanup of state until all existing uses of the object are complete.
+ /// </summary>
+ /// <typeparam name="T">The type of object whose lifetime will be managed.</typeparam>
+ /// <remarks>
+ /// This type maintains a reference count, and tracks whether the object has been disposed. When
+ /// Callbacks are made to <see cref="IDeferredDisposable.OnFinalRelease(bool)"/> when the refcount
+ /// reaches zero. Objects that need to defer cleanup until they have been disposed *and* they have
+ /// no more references can do so in <see cref="IDeferredDisposable.OnFinalRelease(bool)"/> when
+ /// 'disposed' is true.
+ /// </remarks>
+ internal struct DeferredDisposableLifetime<T> where T : class, IDeferredDisposable
+ {
+ //
+ // _count is positive until Dispose is called, after which it's (-1 - refcount).
+ //
+ private int _count;
+
+ public bool AddRef(T obj)
+ {
+ while (true)
+ {
+ int oldCount = Volatile.Read(ref _count);
+
+ // Have we been disposed?
+ if (oldCount < 0)
+ throw new ObjectDisposedException(typeof(T).ToString());
+
+ int newCount = checked(oldCount + 1);
+
+ if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+ return true;
+ }
+ }
+
+ public void Release(T obj)
+ {
+ while (true)
+ {
+ int oldCount = Volatile.Read(ref _count);
+ if (oldCount > 0)
+ {
+ // We haven't been disposed. Decrement _count.
+ int newCount = oldCount - 1;
+ if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+ {
+ if (newCount == 0)
+ obj.OnFinalRelease(disposed: false);
+ return;
+ }
+ }
+ else
+ {
+ Debug.Assert(oldCount != 0 && oldCount != -1);
+
+ // We've been disposed. Increment _count.
+ int newCount = oldCount + 1;
+ if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+ {
+ if (newCount == -1)
+ obj.OnFinalRelease(disposed: true);
+ return;
+ }
+ }
+ }
+ }
+
+ public void Dispose(T obj)
+ {
+ while (true)
+ {
+ int oldCount = Volatile.Read(ref _count);
+ if (oldCount < 0)
+ return; // already disposed
+
+ int newCount = -1 - oldCount;
+ if (Interlocked.CompareExchange(ref _count, newCount, oldCount) == oldCount)
+ {
+ if (newCount == -1)
+ obj.OnFinalRelease(disposed: true);
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/EventResetMode.cs b/src/mscorlib/shared/System/Threading/EventResetMode.cs
new file mode 100644
index 0000000000..7aac0f51eb
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/EventResetMode.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+** Enum: EventResetMode
+**
+**
+** Purpose: Enum to determine the Event type to create
+**
+**
+=============================================================================*/
+
+namespace System.Threading
+{
+ public enum EventResetMode
+ {
+ AutoReset = 0,
+ ManualReset = 1
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ExecutionContext.cs b/src/mscorlib/shared/System/Threading/ExecutionContext.cs
new file mode 100644
index 0000000000..67857e9b11
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ExecutionContext.cs
@@ -0,0 +1,370 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: Capture execution context for a thread
+**
+**
+===========================================================*/
+
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Runtime.ExceptionServices;
+using System.Runtime.Serialization;
+
+using Thread = Internal.Runtime.Augments.RuntimeThread;
+
+namespace System.Threading
+{
+ public delegate void ContextCallback(Object state);
+
+ internal struct ExecutionContextSwitcher
+ {
+ internal ExecutionContext m_ec;
+ internal SynchronizationContext m_sc;
+
+ internal void Undo(Thread currentThread)
+ {
+ Debug.Assert(currentThread == Thread.CurrentThread);
+
+ // The common case is that these have not changed, so avoid the cost of a write if not needed.
+ if (currentThread.SynchronizationContext != m_sc)
+ {
+ currentThread.SynchronizationContext = m_sc;
+ }
+
+ if (currentThread.ExecutionContext != m_ec)
+ {
+ ExecutionContext.Restore(currentThread, m_ec);
+ }
+ }
+ }
+
+ [Serializable]
+ public sealed class ExecutionContext : IDisposable, ISerializable
+ {
+ internal static readonly ExecutionContext Default = new ExecutionContext();
+
+ private readonly IAsyncLocalValueMap m_localValues;
+ private readonly IAsyncLocal[] m_localChangeNotifications;
+ private readonly bool m_isFlowSuppressed;
+
+ private ExecutionContext()
+ {
+ m_localValues = AsyncLocalValueMap.Empty;
+ m_localChangeNotifications = Array.Empty<IAsyncLocal>();
+ }
+
+ private ExecutionContext(
+ IAsyncLocalValueMap localValues,
+ IAsyncLocal[] localChangeNotifications,
+ bool isFlowSuppressed)
+ {
+ m_localValues = localValues;
+ m_localChangeNotifications = localChangeNotifications;
+ m_isFlowSuppressed = isFlowSuppressed;
+ }
+
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ {
+ throw new ArgumentNullException(nameof(info));
+ }
+ Contract.EndContractBlock();
+ }
+
+ private ExecutionContext(SerializationInfo info, StreamingContext context)
+ {
+ }
+
+ public static ExecutionContext Capture()
+ {
+ ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext;
+ return
+ executionContext == null ? Default :
+ executionContext.m_isFlowSuppressed ? null :
+ executionContext;
+ }
+
+ private ExecutionContext ShallowClone(bool isFlowSuppressed)
+ {
+ Debug.Assert(isFlowSuppressed != m_isFlowSuppressed);
+
+ if (!isFlowSuppressed &&
+ m_localValues == Default.m_localValues &&
+ m_localChangeNotifications == Default.m_localChangeNotifications)
+ {
+ return null; // implies the default context
+ }
+ return new ExecutionContext(m_localValues, m_localChangeNotifications, isFlowSuppressed);
+ }
+
+ public static AsyncFlowControl SuppressFlow()
+ {
+ Thread currentThread = Thread.CurrentThread;
+ ExecutionContext executionContext = currentThread.ExecutionContext ?? Default;
+ if (executionContext.m_isFlowSuppressed)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_CannotSupressFlowMultipleTimes);
+ }
+ Contract.EndContractBlock();
+
+ executionContext = executionContext.ShallowClone(isFlowSuppressed: true);
+ var asyncFlowControl = new AsyncFlowControl();
+ currentThread.ExecutionContext = executionContext;
+ asyncFlowControl.Initialize(currentThread);
+ return asyncFlowControl;
+ }
+
+ public static void RestoreFlow()
+ {
+ Thread currentThread = Thread.CurrentThread;
+ ExecutionContext executionContext = currentThread.ExecutionContext;
+ if (executionContext == null || !executionContext.m_isFlowSuppressed)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_CannotRestoreUnsupressedFlow);
+ }
+ Contract.EndContractBlock();
+
+ currentThread.ExecutionContext = executionContext.ShallowClone(isFlowSuppressed: false);
+ }
+
+ public static bool IsFlowSuppressed()
+ {
+ ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext;
+ return executionContext != null && executionContext.m_isFlowSuppressed;
+ }
+
+ public static void Run(ExecutionContext executionContext, ContextCallback callback, Object state)
+ {
+ if (executionContext == null)
+ throw new InvalidOperationException(SR.InvalidOperation_NullContext);
+
+ Thread currentThread = Thread.CurrentThread;
+ ExecutionContextSwitcher ecsw = default(ExecutionContextSwitcher);
+ try
+ {
+ EstablishCopyOnWriteScope(currentThread, ref ecsw);
+ ExecutionContext.Restore(currentThread, executionContext);
+ callback(state);
+ }
+ catch
+ {
+ // Note: we have a "catch" rather than a "finally" because we want
+ // to stop the first pass of EH here. That way we can restore the previous
+ // context before any of our callers' EH filters run. That means we need to
+ // end the scope separately in the non-exceptional case below.
+ ecsw.Undo(currentThread);
+ throw;
+ }
+ ecsw.Undo(currentThread);
+ }
+
+ internal static void Restore(Thread currentThread, ExecutionContext executionContext)
+ {
+ Debug.Assert(currentThread == Thread.CurrentThread);
+
+ ExecutionContext previous = currentThread.ExecutionContext ?? Default;
+ currentThread.ExecutionContext = executionContext;
+
+ // New EC could be null if that's what ECS.Undo saved off.
+ // For the purposes of dealing with context change, treat this as the default EC
+ executionContext = executionContext ?? Default;
+
+ if (previous != executionContext)
+ {
+ OnContextChanged(previous, executionContext);
+ }
+ }
+
+ internal static void EstablishCopyOnWriteScope(Thread currentThread, ref ExecutionContextSwitcher ecsw)
+ {
+ Debug.Assert(currentThread == Thread.CurrentThread);
+
+ ecsw.m_ec = currentThread.ExecutionContext;
+ ecsw.m_sc = currentThread.SynchronizationContext;
+ }
+
+ private static void OnContextChanged(ExecutionContext previous, ExecutionContext current)
+ {
+ Debug.Assert(previous != null);
+ Debug.Assert(current != null);
+ Debug.Assert(previous != current);
+
+ foreach (IAsyncLocal local in previous.m_localChangeNotifications)
+ {
+ object previousValue;
+ object currentValue;
+ previous.m_localValues.TryGetValue(local, out previousValue);
+ current.m_localValues.TryGetValue(local, out currentValue);
+
+ if (previousValue != currentValue)
+ local.OnValueChanged(previousValue, currentValue, true);
+ }
+
+ if (current.m_localChangeNotifications != previous.m_localChangeNotifications)
+ {
+ try
+ {
+ foreach (IAsyncLocal local in current.m_localChangeNotifications)
+ {
+ // If the local has a value in the previous context, we already fired the event for that local
+ // in the code above.
+ object previousValue;
+ if (!previous.m_localValues.TryGetValue(local, out previousValue))
+ {
+ object currentValue;
+ current.m_localValues.TryGetValue(local, out currentValue);
+
+ if (previousValue != currentValue)
+ local.OnValueChanged(previousValue, currentValue, true);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Environment.FailFast(
+ SR.ExecutionContext_ExceptionInAsyncLocalNotification,
+ ex);
+ }
+ }
+ }
+
+ internal static object GetLocalValue(IAsyncLocal local)
+ {
+ ExecutionContext current = Thread.CurrentThread.ExecutionContext;
+ if (current == null)
+ return null;
+
+ object value;
+ current.m_localValues.TryGetValue(local, out value);
+ return value;
+ }
+
+ internal static void SetLocalValue(IAsyncLocal local, object newValue, bool needChangeNotifications)
+ {
+ ExecutionContext current = Thread.CurrentThread.ExecutionContext ?? ExecutionContext.Default;
+
+ object previousValue;
+ bool hadPreviousValue = current.m_localValues.TryGetValue(local, out previousValue);
+
+ if (previousValue == newValue)
+ return;
+
+ IAsyncLocalValueMap newValues = current.m_localValues.Set(local, newValue);
+
+ //
+ // Either copy the change notification array, or create a new one, depending on whether we need to add a new item.
+ //
+ IAsyncLocal[] newChangeNotifications = current.m_localChangeNotifications;
+ if (needChangeNotifications)
+ {
+ if (hadPreviousValue)
+ {
+ Debug.Assert(Array.IndexOf(newChangeNotifications, local) >= 0);
+ }
+ else
+ {
+ int newNotificationIndex = newChangeNotifications.Length;
+ Array.Resize(ref newChangeNotifications, newNotificationIndex + 1);
+ newChangeNotifications[newNotificationIndex] = local;
+ }
+ }
+
+ Thread.CurrentThread.ExecutionContext =
+ new ExecutionContext(newValues, newChangeNotifications, current.m_isFlowSuppressed);
+
+ if (needChangeNotifications)
+ {
+ local.OnValueChanged(previousValue, newValue, false);
+ }
+ }
+
+ public ExecutionContext CreateCopy()
+ {
+ return this; // since CoreCLR's ExecutionContext is immutable, we don't need to create copies.
+ }
+
+ public void Dispose()
+ {
+ // For CLR compat only
+ }
+ }
+
+ public struct AsyncFlowControl : IDisposable
+ {
+ private Thread _thread;
+
+ internal void Initialize(Thread currentThread)
+ {
+ Debug.Assert(currentThread == Thread.CurrentThread);
+ _thread = currentThread;
+ }
+
+ public void Undo()
+ {
+ if (_thread == null)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_CannotUseAFCMultiple);
+ }
+ if (Thread.CurrentThread != _thread)
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_CannotUseAFCOtherThread);
+ }
+
+ // An async flow control cannot be undone when a different execution context is applied. The desktop framework
+ // mutates the execution context when its state changes, and only changes the instance when an execution context
+ // is applied (for instance, through ExecutionContext.Run). The framework prevents a suppressed-flow execution
+ // context from being applied by returning null from ExecutionContext.Capture, so the only type of execution
+ // context that can be applied is one whose flow is not suppressed. After suppressing flow and changing an async
+ // local's value, the desktop framework verifies that a different execution context has not been applied by
+ // checking the execution context instance against the one saved from when flow was suppressed. In .NET Core,
+ // since the execution context instance will change after changing the async local's value, it verifies that a
+ // different execution context has not been applied, by instead ensuring that the current execution context's
+ // flow is suppressed.
+ if (!ExecutionContext.IsFlowSuppressed())
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_AsyncFlowCtrlCtxMismatch);
+ }
+ Contract.EndContractBlock();
+
+ _thread = null;
+ ExecutionContext.RestoreFlow();
+ }
+
+ public void Dispose()
+ {
+ Undo();
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is AsyncFlowControl && Equals((AsyncFlowControl)obj);
+ }
+
+ public bool Equals(AsyncFlowControl obj)
+ {
+ return _thread == obj._thread;
+ }
+
+ public override int GetHashCode()
+ {
+ return _thread?.GetHashCode() ?? 0;
+ }
+
+ public static bool operator ==(AsyncFlowControl a, AsyncFlowControl b)
+ {
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(AsyncFlowControl a, AsyncFlowControl b)
+ {
+ return !(a == b);
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/LazyThreadSafetyMode.cs b/src/mscorlib/shared/System/Threading/LazyThreadSafetyMode.cs
new file mode 100644
index 0000000000..2d13f23762
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/LazyThreadSafetyMode.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// a set of lightweight static helpers for lazy initialization.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+namespace System.Threading
+{
+ /// <summary>
+ /// Specifies how a <see cref="T:System.Threading.Lazy{T}"/> instance should synchronize access among multiple threads.
+ /// </summary>
+ public enum LazyThreadSafetyMode
+ {
+ /// <summary>
+ /// This mode makes no guarantees around the thread-safety of the <see cref="T:System.Threading.Lazy{T}"/> instance. If used from multiple threads, the behavior of the <see cref="T:System.Threading.Lazy{T}"/> is undefined.
+ /// This mode should be used when a <see cref="T:System.Threading.Lazy{T}"/> is guaranteed to never be initialized from more than one thread simultaneously and high performance is crucial.
+ /// If valueFactory throws an exception when the <see cref="T:System.Threading.Lazy{T}"/> is initialized, the exception will be cached and returned on subsequent accesses to Value. Also, if valueFactory recursively
+ /// accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, a <see cref="T:System.InvalidOperationException"/> will be thrown.
+ /// </summary>
+ None,
+
+ /// <summary>
+ /// When multiple threads attempt to simultaneously initialize a <see cref="T:System.Threading.Lazy{T}"/> instance, this mode allows each thread to execute the
+ /// valueFactory but only the first thread to complete initialization will be allowed to set the final value of the <see cref="T:System.Threading.Lazy{T}"/>.
+ /// Once initialized successfully, any future calls to Value will return the cached result. If valueFactory throws an exception on any thread, that exception will be
+ /// propagated out of Value. If any thread executes valueFactory without throwing an exception and, therefore, successfully sets the value, that value will be returned on
+ /// subsequent accesses to Value from any thread. If no thread succeeds in setting the value, IsValueCreated will remain false and subsequent accesses to Value will result in
+ /// the valueFactory delegate re-executing. Also, if valueFactory recursively accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, an exception will NOT be thrown.
+ /// </summary>
+ PublicationOnly,
+
+ /// <summary>
+ /// This mode uses locks to ensure that only a single thread can initialize a <see cref="T:System.Threading.Lazy{T}"/> instance in a thread-safe manner. In general,
+ /// taken if this mode is used in conjunction with a <see cref="T:System.Threading.Lazy{T}"/> valueFactory delegate that uses locks internally, a deadlock can occur if not
+ /// handled carefully. If valueFactory throws an exception when the<see cref="T:System.Threading.Lazy{T}"/> is initialized, the exception will be cached and returned on
+ /// subsequent accesses to Value. Also, if valueFactory recursively accesses Value on this <see cref="T:System.Threading.Lazy{T}"/> instance, a <see cref="T:System.InvalidOperationException"/> will be thrown.
+ /// </summary>
+ ExecutionAndPublication
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/LockRecursionException.cs b/src/mscorlib/shared/System/Threading/LockRecursionException.cs
new file mode 100644
index 0000000000..2f296cb14e
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/LockRecursionException.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class LockRecursionException : System.Exception
+ {
+ public LockRecursionException()
+ {
+ }
+
+ public LockRecursionException(string message)
+ : base(message)
+ {
+ }
+
+ public LockRecursionException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ protected LockRecursionException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ManualResetEvent.cs b/src/mscorlib/shared/System/Threading/ManualResetEvent.cs
new file mode 100644
index 0000000000..4b8d61f960
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ManualResetEvent.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading
+{
+ public sealed class ManualResetEvent : EventWaitHandle
+ {
+ public ManualResetEvent(bool initialState) : base(initialState, EventResetMode.ManualReset) { }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Threading/ParameterizedThreadStart.cs b/src/mscorlib/shared/System/Threading/ParameterizedThreadStart.cs
new file mode 100644
index 0000000000..c0f29e8e80
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ParameterizedThreadStart.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: This class is a Delegate which defines the start method
+** for starting a thread. That method must match this delegate.
+**
+**
+=============================================================================*/
+
+namespace System.Threading
+{
+ public delegate void ParameterizedThreadStart(object obj);
+}
diff --git a/src/mscorlib/shared/System/Threading/SemaphoreFullException.cs b/src/mscorlib/shared/System/Threading/SemaphoreFullException.cs
new file mode 100644
index 0000000000..19ac19d6e0
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/SemaphoreFullException.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class SemaphoreFullException : SystemException
+ {
+ public SemaphoreFullException() : base(SR.Threading_SemaphoreFullException)
+ {
+ }
+
+ public SemaphoreFullException(String message) : base(message)
+ {
+ }
+
+ public SemaphoreFullException(String message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ protected SemaphoreFullException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/SendOrPostCallback.cs b/src/mscorlib/shared/System/Threading/SendOrPostCallback.cs
new file mode 100644
index 0000000000..6692d35ab2
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/SendOrPostCallback.cs
@@ -0,0 +1,8 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading
+{
+ public delegate void SendOrPostCallback(Object state);
+}
diff --git a/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs b/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs
new file mode 100644
index 0000000000..120577fdcf
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/SynchronizationLockException.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Wait(), Notify() or NotifyAll() was called from an unsynchronized
+** block of code.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class SynchronizationLockException : SystemException
+ {
+ public SynchronizationLockException()
+ : base(SR.Arg_SynchronizationLockException)
+ {
+ HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK;
+ }
+
+ public SynchronizationLockException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK;
+ }
+
+ public SynchronizationLockException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_SYNCHRONIZATIONLOCK;
+ }
+
+ protected SynchronizationLockException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/Tasks/TaskCanceledException.cs b/src/mscorlib/shared/System/Threading/Tasks/TaskCanceledException.cs
new file mode 100644
index 0000000000..d7690d4c7c
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Tasks/TaskCanceledException.cs
@@ -0,0 +1,89 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// An exception for task cancellations.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Represents an exception used to communicate task cancellation.
+ /// </summary>
+ [Serializable]
+ public class TaskCanceledException : OperationCanceledException
+ {
+ [NonSerialized]
+ private Task m_canceledTask; // The task which has been canceled.
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> class.
+ /// </summary>
+ public TaskCanceledException() : base(SR.TaskCanceledException_ctor_DefaultMessage)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/>
+ /// class with a specified error message.
+ /// </summary>
+ /// <param name="message">The error message that explains the reason for the exception.</param>
+ public TaskCanceledException(string message) : base(message)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/>
+ /// class with a specified error message and a reference to the inner exception that is the cause of
+ /// this exception.
+ /// </summary>
+ /// <param name="message">The error message that explains the reason for the exception.</param>
+ /// <param name="innerException">The exception that is the cause of the current exception.</param>
+ public TaskCanceledException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/> class
+ /// with a reference to the <see cref="T:System.Threading.Tasks.Task"/> that has been canceled.
+ /// </summary>
+ /// <param name="task">A task that has been canceled.</param>
+ public TaskCanceledException(Task task) :
+ base(SR.TaskCanceledException_ctor_DefaultMessage, task != null ? task.CancellationToken : new CancellationToken())
+ {
+ m_canceledTask = task;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskCanceledException"/>
+ /// class with serialized data.
+ /// </summary>
+ /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination. </param>
+ protected TaskCanceledException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+
+ /// <summary>
+ /// Gets the task associated with this exception.
+ /// </summary>
+ /// <remarks>
+ /// It is permissible for no Task to be associated with a
+ /// <see cref="T:System.Threading.Tasks.TaskCanceledException"/>, in which case
+ /// this property will return null.
+ /// </remarks>
+ public Task Task
+ {
+ get { return m_canceledTask; }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs b/src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs
new file mode 100644
index 0000000000..1098299517
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Tasks/TaskExtensions.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading.Tasks
+{
+ /// <summary>Provides a set of static methods for working with specific kinds of <see cref="Task"/> instances.</summary>
+ public static class TaskExtensions
+ {
+ /// <summary>Creates a proxy <see cref="Task"/> that represents the asynchronous operation of a <see cref="Task{Task}"/>.</summary>
+ /// <param name="task">The <see cref="Task{Task}"/> to unwrap.</param>
+ /// <returns>A <see cref="Task"/> that represents the asynchronous operation of the provided <see cref="Task{Task}"/>.</returns>
+ public static Task Unwrap(this Task<Task> task)
+ {
+ if (task == null)
+ {
+ throw new ArgumentNullException(nameof(task));
+ }
+
+ // If the task hasn't completed or was faulted/canceled, wrap it in an unwrap promise. Otherwise,
+ // it completed successfully. Return its inner task to avoid unnecessary wrapping, or if the inner
+ // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise.
+ return
+ !task.IsRanToCompletion ? Task.CreateUnwrapPromise<VoidTaskResult>(task, lookForOce: false) :
+ task.Result ??
+ Task.FromCanceled(new CancellationToken(true));
+ }
+
+ /// <summary>Creates a proxy <see cref="Task{TResult}"/> that represents the asynchronous operation of a <see cref="Task{Task{TResult}}"/>.</summary>
+ /// <param name="task">The <see cref="Task{Task{TResult}}"/> to unwrap.</param>
+ /// <returns>A <see cref="Task{TResult}"/> that represents the asynchronous operation of the provided <see cref="Task{Task{TResult}}"/>.</returns>
+ public static Task<TResult> Unwrap<TResult>(this Task<Task<TResult>> task)
+ {
+ if (task == null)
+ {
+ throw new ArgumentNullException(nameof(task));
+ }
+
+ // If the task hasn't completed or was faulted/canceled, wrap it in an unwrap promise. Otherwise,
+ // it completed successfully. Return its inner task to avoid unnecessary wrapping, or if the inner
+ // task is null, return a canceled task to match the same semantics as CreateUnwrapPromise.
+ return
+ !task.IsRanToCompletion ? Task.CreateUnwrapPromise<TResult>(task, lookForOce: false) :
+ task.Result ??
+ Task.FromCanceled<TResult>(new CancellationToken(true));
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/Tasks/TaskSchedulerException.cs b/src/mscorlib/shared/System/Threading/Tasks/TaskSchedulerException.cs
new file mode 100644
index 0000000000..148b6300ef
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Tasks/TaskSchedulerException.cs
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+//
+//
+// An exception for task schedulers.
+//
+// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace System.Threading.Tasks
+{
+ /// <summary>
+ /// Represents an exception used to communicate an invalid operation by a
+ /// <see cref="T:System.Threading.Tasks.TaskScheduler"/>.
+ /// </summary>
+ [Serializable]
+ public class TaskSchedulerException : Exception
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/> class.
+ /// </summary>
+ public TaskSchedulerException() : base(SR.TaskSchedulerException_ctor_DefaultMessage) //
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/>
+ /// class with a specified error message.
+ /// </summary>
+ /// <param name="message">The error message that explains the reason for the exception.</param>
+ public TaskSchedulerException(string message) : base(message)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/>
+ /// class using the default error message and a reference to the inner exception that is the cause of
+ /// this exception.
+ /// </summary>
+ /// <param name="innerException">The exception that is the cause of the current exception.</param>
+ public TaskSchedulerException(Exception innerException)
+ : base(SR.TaskSchedulerException_ctor_DefaultMessage, innerException)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/>
+ /// class with a specified error message and a reference to the inner exception that is the cause of
+ /// this exception.
+ /// </summary>
+ /// <param name="message">The error message that explains the reason for the exception.</param>
+ /// <param name="innerException">The exception that is the cause of the current exception.</param>
+ public TaskSchedulerException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:System.Threading.Tasks.TaskSchedulerException"/>
+ /// class with serialized data.
+ /// </summary>
+ /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds
+ /// the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that
+ /// contains contextual information about the source or destination. </param>
+ protected TaskSchedulerException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadAbortException.cs b/src/mscorlib/shared/System/Threading/ThreadAbortException.cs
new file mode 100644
index 0000000000..e693e7192f
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadAbortException.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: An exception class which is thrown into a thread to cause it to
+** abort. This is a special non-catchable exception and results in
+** the thread's death. This is thrown by the VM only and can NOT be
+** thrown by any user thread, and subclassing this is useless.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public sealed class ThreadAbortException : SystemException
+ {
+ private ThreadAbortException()
+ {
+ HResult = __HResults.COR_E_THREADABORTED;
+ }
+
+ internal ThreadAbortException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ public object ExceptionState => null;
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadPriority.cs b/src/mscorlib/shared/System/Threading/ThreadPriority.cs
new file mode 100644
index 0000000000..3b34bd5eac
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadPriority.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading
+{
+ public enum ThreadPriority
+ {
+ /*=========================================================================
+ ** Constants for thread priorities.
+ =========================================================================*/
+ Lowest = 0,
+ BelowNormal = 1,
+ Normal = 2,
+ AboveNormal = 3,
+ Highest = 4
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadStart.cs b/src/mscorlib/shared/System/Threading/ThreadStart.cs
new file mode 100644
index 0000000000..5532539fc7
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadStart.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: This class is a Delegate which defines the start method
+** for starting a thread. That method must match this delegate.
+**
+**
+=============================================================================*/
+
+namespace System.Threading
+{
+ public delegate void ThreadStart();
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadStartException.cs b/src/mscorlib/shared/System/Threading/ThreadStartException.cs
new file mode 100644
index 0000000000..2ff77bc5fd
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadStartException.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public sealed class ThreadStartException : SystemException
+ {
+ internal ThreadStartException()
+ : base(SR.Arg_ThreadStartException)
+ {
+ HResult = __HResults.COR_E_THREADSTART;
+ }
+
+ internal ThreadStartException(Exception reason)
+ : base(SR.Arg_ThreadStartException, reason)
+ {
+ HResult = __HResults.COR_E_THREADSTART;
+ }
+
+ internal ThreadStartException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadState.cs b/src/mscorlib/shared/System/Threading/ThreadState.cs
new file mode 100644
index 0000000000..4bf3b5184d
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadState.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Threading
+{
+ [Flags]
+ public enum ThreadState
+ {
+ /*=========================================================================
+ ** Constants for thread states.
+ =========================================================================*/
+ Running = 0,
+ StopRequested = 1,
+ SuspendRequested = 2,
+ Background = 4,
+ Unstarted = 8,
+ Stopped = 16,
+ WaitSleepJoin = 32,
+ Suspended = 64,
+ AbortRequested = 128,
+ Aborted = 256
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/ThreadStateException.cs b/src/mscorlib/shared/System/Threading/ThreadStateException.cs
new file mode 100644
index 0000000000..33bc8baee6
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/ThreadStateException.cs
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: An exception class to indicate that the Thread class is in an
+** invalid state for the method.
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class ThreadStateException : SystemException
+ {
+ public ThreadStateException()
+ : base(SR.Arg_ThreadStateException)
+ {
+ HResult = __HResults.COR_E_THREADSTATE;
+ }
+
+ public ThreadStateException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_THREADSTATE;
+ }
+
+ public ThreadStateException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_THREADSTATE;
+ }
+
+ protected ThreadStateException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/Timeout.cs b/src/mscorlib/shared/System/Threading/Timeout.cs
new file mode 100644
index 0000000000..df1ea5f2bc
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/Timeout.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Threading;
+using System;
+
+namespace System.Threading
+{
+ // A constant used by methods that take a timeout (Object.Wait, Thread.Sleep
+ // etc) to indicate that no timeout should occur.
+ //
+ public static class Timeout
+ {
+ public static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, Timeout.Infinite);
+
+ public const int Infinite = -1;
+ internal const uint UnsignedInfinite = unchecked((uint)-1);
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/TimeoutHelper.cs b/src/mscorlib/shared/System/Threading/TimeoutHelper.cs
new file mode 100644
index 0000000000..c66c9add92
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/TimeoutHelper.cs
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Diagnostics;
+
+namespace System.Threading
+{
+ /// <summary>
+ /// A helper class to capture a start time using Environment.TickCout as a time in milliseconds, also updates a given timeout bu subtracting the current time from
+ /// the start time
+ /// </summary>
+ internal static class TimeoutHelper
+ {
+ /// <summary>
+ /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from postive to negative every ~ 25 days
+ /// then ~25 days to back to positive again, uint is sued to ignore the sign and double the range to 50 days
+ /// </summary>
+ /// <returns></returns>
+ public static uint GetTime()
+ {
+ return (uint)Environment.TickCount;
+ }
+
+ /// <summary>
+ /// Helper function to measure and update the elapsed time
+ /// </summary>
+ /// <param name="startTime"> The first time (in milliseconds) observed when the wait started</param>
+ /// <param name="originalWaitMillisecondsTimeout">The orginal wait timeoutout in milliseconds</param>
+ /// <returns>The new wait time in milliseconds, -1 if the time expired</returns>
+ public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout)
+ {
+ // The function must be called in case the time out is not infinite
+ Debug.Assert(originalWaitMillisecondsTimeout != Timeout.Infinite);
+
+ uint elapsedMilliseconds = (GetTime() - startTime);
+
+ // Check the elapsed milliseconds is greater than max int because this property is uint
+ if (elapsedMilliseconds > int.MaxValue)
+ {
+ return 0;
+ }
+
+ // Subtract the elapsed time from the current wait time
+ int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; ;
+ if (currentWaitTimeout <= 0)
+ {
+ return 0;
+ }
+
+ return currentWaitTimeout;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs b/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs
new file mode 100644
index 0000000000..e44946a669
--- /dev/null
+++ b/src/mscorlib/shared/System/Threading/WaitHandleCannotBeOpenedException.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System.Threading
+{
+ [Serializable]
+ public class WaitHandleCannotBeOpenedException : ApplicationException
+ {
+ public WaitHandleCannotBeOpenedException() : base(SR.Threading_WaitHandleCannotBeOpenedException)
+ {
+ HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED;
+ }
+
+ public WaitHandleCannotBeOpenedException(String message) : base(message)
+ {
+ HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED;
+ }
+
+ public WaitHandleCannotBeOpenedException(String message, Exception innerException) : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_WAITHANDLECANNOTBEOPENED;
+ }
+
+ protected WaitHandleCannotBeOpenedException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/TimeZone.cs b/src/mscorlib/shared/System/TimeZone.cs
new file mode 100644
index 0000000000..88e2e21864
--- /dev/null
+++ b/src/mscorlib/shared/System/TimeZone.cs
@@ -0,0 +1,281 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose:
+** This class is used to represent a TimeZone. It
+** has methods for converting a DateTime to UTC from local time
+** and to local time from UTC and methods for getting the
+** standard name and daylight name of the time zone.
+**
+** The only TimeZone that we support in version 1 is the
+** CurrentTimeZone as determined by the system timezone.
+**
+**
+============================================================*/
+
+using System;
+using System.Text;
+using System.Threading;
+using System.Collections;
+using System.Globalization;
+
+namespace System
+{
+ [Serializable]
+ [Obsolete("System.TimeZone has been deprecated. Please investigate the use of System.TimeZoneInfo instead.")]
+ public abstract class TimeZone
+ {
+ private static volatile TimeZone currentTimeZone = null;
+
+ // Private object for locking instead of locking on a public type for SQL reliability work.
+ private static Object s_InternalSyncObject;
+ private static Object InternalSyncObject
+ {
+ get
+ {
+ if (s_InternalSyncObject == null)
+ {
+ Object o = new Object();
+ Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null);
+ }
+ return s_InternalSyncObject;
+ }
+ }
+
+
+ protected TimeZone()
+ {
+ }
+
+ public static TimeZone CurrentTimeZone
+ {
+ get
+ {
+ //Grabbing the cached value is required at the top of this function so that
+ //we don't incur a race condition with the ResetTimeZone method below.
+ TimeZone tz = currentTimeZone;
+ if (tz == null)
+ {
+ lock (InternalSyncObject)
+ {
+ if (currentTimeZone == null)
+ {
+ currentTimeZone = new CurrentSystemTimeZone();
+ }
+ tz = currentTimeZone;
+ }
+ }
+ return (tz);
+ }
+ }
+
+ //This method is called by CultureInfo.ClearCachedData in response to control panel
+ //change events. It must be synchronized because otherwise there is a race condition
+ //with the CurrentTimeZone property above.
+ internal static void ResetTimeZone()
+ {
+ if (currentTimeZone != null)
+ {
+ lock (InternalSyncObject)
+ {
+ currentTimeZone = null;
+ }
+ }
+ }
+
+ public abstract String StandardName
+ {
+ get;
+ }
+
+ public abstract String DaylightName
+ {
+ get;
+ }
+
+ public abstract TimeSpan GetUtcOffset(DateTime time);
+
+ //
+ // Converts the specified datatime to the Universal time base on the current timezone
+ //
+ public virtual DateTime ToUniversalTime(DateTime time)
+ {
+ if (time.Kind == DateTimeKind.Utc)
+ {
+ return time;
+ }
+ long tickCount = time.Ticks - GetUtcOffset(time).Ticks;
+ if (tickCount > DateTime.MaxTicks)
+ {
+ return new DateTime(DateTime.MaxTicks, DateTimeKind.Utc);
+ }
+ if (tickCount < DateTime.MinTicks)
+ {
+ return new DateTime(DateTime.MinTicks, DateTimeKind.Utc);
+ }
+ return new DateTime(tickCount, DateTimeKind.Utc);
+ }
+
+ //
+ // Convert the specified datetime value from UTC to the local time based on the time zone.
+ //
+ public virtual DateTime ToLocalTime(DateTime time)
+ {
+ if (time.Kind == DateTimeKind.Local)
+ {
+ return time;
+ }
+ Boolean isAmbiguousLocalDst = false;
+ Int64 offset = ((CurrentSystemTimeZone)(TimeZone.CurrentTimeZone)).GetUtcOffsetFromUniversalTime(time, ref isAmbiguousLocalDst);
+ return new DateTime(time.Ticks + offset, DateTimeKind.Local, isAmbiguousLocalDst);
+ }
+
+ // Return an array of DaylightTime which reflects the daylight saving periods in a particular year.
+ // We currently only support having one DaylightSavingTime per year.
+ // If daylight saving time is not used in this timezone, null will be returned.
+ public abstract DaylightTime GetDaylightChanges(int year);
+
+ public virtual bool IsDaylightSavingTime(DateTime time)
+ {
+ return (IsDaylightSavingTime(time, GetDaylightChanges(time.Year)));
+ }
+
+ // Check if the specified time is in a daylight saving time. Allows the user to
+ // specify the array of Daylight Saving Times.
+ public static bool IsDaylightSavingTime(DateTime time, DaylightTime daylightTimes)
+ {
+ return CalculateUtcOffset(time, daylightTimes) != TimeSpan.Zero;
+ }
+
+ //
+ // NOTENOTE: Implementation detail
+ // In the transition from standard time to daylight saving time,
+ // if we convert local time to Universal time, we can have the
+ // following (take PST as an example):
+ // Local Universal UTC Offset
+ // ----- --------- ----------
+ // 01:00AM 09:00 -8:00
+ // 02:00 (=> 03:00) 10:00 -8:00 [This time doesn't actually exist, but it can be created from DateTime]
+ // 03:00 10:00 -7:00
+ // 04:00 11:00 -7:00
+ // 05:00 12:00 -7:00
+ //
+ // So from 02:00 - 02:59:59, we should return the standard offset, instead of the daylight saving offset.
+ //
+ // In the transition from daylight saving time to standard time,
+ // if we convert local time to Universal time, we can have the
+ // following (take PST as an example):
+ // Local Universal UTC Offset
+ // ----- --------- ----------
+ // 01:00AM 08:00 -7:00
+ // 02:00 (=> 01:00) 09:00 -8:00
+ // 02:00 10:00 -8:00
+ // 03:00 11:00 -8:00
+ // 04:00 12:00 -8:00
+ //
+ // So in this case, the 02:00 does exist after the first 2:00 rolls back to 01:00. We don't need to special case this.
+ // But note that there are two 01:00 in the local time.
+
+ //
+ // And imagine if the daylight saving offset is negative (although this does not exist in real life)
+ // In the transition from standard time to daylight saving time,
+ // if we convert local time to Universal time, we can have the
+ // following (take PST as an example, but the daylight saving offset is -01:00):
+ // Local Universal UTC Offset
+ // ----- --------- ----------
+ // 01:00AM 09:00 -8:00
+ // 02:00 (=> 01:00) 10:00 -9:00
+ // 02:00 11:00 -9:00
+ // 03:00 12:00 -9:00
+ // 04:00 13:00 -9:00
+ // 05:00 14:00 -9:00
+ //
+ // So in this case, the 02:00 does exist after the first 2:00 rolls back to 01:00. We don't need to special case this.
+ //
+ // In the transition from daylight saving time to standard time,
+ // if we convert local time to Universal time, we can have the
+ // following (take PST as an example, daylight saving offset is -01:00):
+ //
+ // Local Universal UTC Offset
+ // ----- --------- ----------
+ // 01:00AM 10:00 -9:00
+ // 02:00 (=> 03:00) 11:00 -9:00
+ // 03:00 11:00 -8:00
+ // 04:00 12:00 -8:00
+ // 05:00 13:00 -8:00
+ // 06:00 14:00 -8:00
+ //
+ // So from 02:00 - 02:59:59, we should return the daylight saving offset, instead of the standard offset.
+ //
+ internal static TimeSpan CalculateUtcOffset(DateTime time, DaylightTime daylightTimes)
+ {
+ if (daylightTimes == null)
+ {
+ return TimeSpan.Zero;
+ }
+ DateTimeKind kind = time.Kind;
+ if (kind == DateTimeKind.Utc)
+ {
+ return TimeSpan.Zero;
+ }
+
+ DateTime startTime;
+ DateTime endTime;
+
+ // startTime and endTime represent the period from either the start of DST to the end and includes the
+ // potentially overlapped times
+ startTime = daylightTimes.Start + daylightTimes.Delta;
+ endTime = daylightTimes.End;
+
+ // For normal time zones, the ambiguous hour is the last hour of daylight saving when you wind the
+ // clock back. It is theoretically possible to have a positive delta, (which would really be daylight
+ // reduction time), where you would have to wind the clock back in the begnning.
+ DateTime ambiguousStart;
+ DateTime ambiguousEnd;
+ if (daylightTimes.Delta.Ticks > 0)
+ {
+ ambiguousStart = endTime - daylightTimes.Delta;
+ ambiguousEnd = endTime;
+ }
+ else
+ {
+ ambiguousStart = startTime;
+ ambiguousEnd = startTime - daylightTimes.Delta;
+ }
+
+ Boolean isDst = false;
+ if (startTime > endTime)
+ {
+ // In southern hemisphere, the daylight saving time starts later in the year, and ends in the beginning of next year.
+ // Note, the summer in the southern hemisphere begins late in the year.
+ if (time >= startTime || time < endTime)
+ {
+ isDst = true;
+ }
+ }
+ else if (time >= startTime && time < endTime)
+ {
+ // In northern hemisphere, the daylight saving time starts in the middle of the year.
+ isDst = true;
+ }
+
+ // If this date was previously converted from a UTC date and we were able to detect that the local
+ // DateTime would be ambiguous, this data is stored in the DateTime to resolve this ambiguity.
+ if (isDst && time >= ambiguousStart && time < ambiguousEnd)
+ {
+ isDst = time.IsAmbiguousDaylightSavingTime();
+ }
+
+ if (isDst)
+ {
+ return daylightTimes.Delta;
+ }
+ return TimeSpan.Zero;
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/TimeZoneNotFoundException.cs b/src/mscorlib/shared/System/TimeZoneNotFoundException.cs
new file mode 100644
index 0000000000..ee21c2524f
--- /dev/null
+++ b/src/mscorlib/shared/System/TimeZoneNotFoundException.cs
@@ -0,0 +1,28 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class TimeZoneNotFoundException : Exception
+ {
+ public TimeZoneNotFoundException()
+ {
+ }
+
+ public TimeZoneNotFoundException(String message)
+ : base(message)
+ {
+ }
+
+ public TimeZoneNotFoundException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ protected TimeZoneNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/TimeoutException.cs b/src/mscorlib/shared/System/TimeoutException.cs
new file mode 100644
index 0000000000..32775a1c56
--- /dev/null
+++ b/src/mscorlib/shared/System/TimeoutException.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: Exception class for Timeout
+**
+**
+=============================================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class TimeoutException : SystemException
+ {
+ public TimeoutException()
+ : base(SR.Arg_TimeoutException)
+ {
+ HResult = __HResults.COR_E_TIMEOUT;
+ }
+
+ public TimeoutException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_TIMEOUT;
+ }
+
+ public TimeoutException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_TIMEOUT;
+ }
+
+ protected TimeoutException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/TupleExtensions.cs b/src/mscorlib/shared/System/TupleExtensions.cs
new file mode 100644
index 0000000000..106a88a08b
--- /dev/null
+++ b/src/mscorlib/shared/System/TupleExtensions.cs
@@ -0,0 +1,930 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace System
+{
+ /// <summary>
+ /// Provides extension methods for <see cref="Tuple"/> instances to interop with C# tuples features (deconstruction syntax, converting from and to <see cref="ValueTuple"/>).
+ /// </summary>
+ public static class TupleExtensions
+ {
+ #region Deconstruct
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 1 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1>(
+ this Tuple<T1> value,
+ out T1 item1)
+ {
+ item1 = value.Item1;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 2 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2>(
+ this Tuple<T1, T2> value,
+ out T1 item1, out T2 item2)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 3 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3>(
+ this Tuple<T1, T2, T3> value,
+ out T1 item1, out T2 item2, out T3 item3)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 4 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4>(
+ this Tuple<T1, T2, T3, T4> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 5 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5>(
+ this Tuple<T1, T2, T3, T4, T5> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 6 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6>(
+ this Tuple<T1, T2, T3, T4, T5, T6> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 7 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 8 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 9 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 10 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 11 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 12 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 13 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ item13 = value.Rest.Item6;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 14 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ item13 = value.Rest.Item6;
+ item14 = value.Rest.Item7;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 15 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15>>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ item13 = value.Rest.Item6;
+ item14 = value.Rest.Item7;
+ item15 = value.Rest.Rest.Item1;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 16 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16>>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ item13 = value.Rest.Item6;
+ item14 = value.Rest.Item7;
+ item15 = value.Rest.Rest.Item1;
+ item16 = value.Rest.Rest.Item2;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 17 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17>>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ item13 = value.Rest.Item6;
+ item14 = value.Rest.Item7;
+ item15 = value.Rest.Rest.Item1;
+ item16 = value.Rest.Rest.Item2;
+ item17 = value.Rest.Rest.Item3;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 18 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18>>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17, out T18 item18)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ item13 = value.Rest.Item6;
+ item14 = value.Rest.Item7;
+ item15 = value.Rest.Rest.Item1;
+ item16 = value.Rest.Rest.Item2;
+ item17 = value.Rest.Rest.Item3;
+ item18 = value.Rest.Rest.Item4;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 19 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18, T19>>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17, out T18 item18, out T19 item19)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ item13 = value.Rest.Item6;
+ item14 = value.Rest.Item7;
+ item15 = value.Rest.Rest.Item1;
+ item16 = value.Rest.Rest.Item2;
+ item17 = value.Rest.Rest.Item3;
+ item18 = value.Rest.Rest.Item4;
+ item19 = value.Rest.Rest.Item5;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 20 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18, T19, T20>>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17, out T18 item18, out T19 item19, out T20 item20)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ item13 = value.Rest.Item6;
+ item14 = value.Rest.Item7;
+ item15 = value.Rest.Rest.Item1;
+ item16 = value.Rest.Rest.Item2;
+ item17 = value.Rest.Rest.Item3;
+ item18 = value.Rest.Rest.Item4;
+ item19 = value.Rest.Rest.Item5;
+ item20 = value.Rest.Rest.Item6;
+ }
+
+ /// <summary>
+ /// Deconstruct a properly nested <see cref="Tuple"/> with 21 elements.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void Deconstruct<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18, T19, T20, T21>>> value,
+ out T1 item1, out T2 item2, out T3 item3, out T4 item4, out T5 item5, out T6 item6, out T7 item7, out T8 item8, out T9 item9, out T10 item10, out T11 item11, out T12 item12, out T13 item13, out T14 item14, out T15 item15, out T16 item16, out T17 item17, out T18 item18, out T19 item19, out T20 item20, out T21 item21)
+ {
+ item1 = value.Item1;
+ item2 = value.Item2;
+ item3 = value.Item3;
+ item4 = value.Item4;
+ item5 = value.Item5;
+ item6 = value.Item6;
+ item7 = value.Item7;
+ item8 = value.Rest.Item1;
+ item9 = value.Rest.Item2;
+ item10 = value.Rest.Item3;
+ item11 = value.Rest.Item4;
+ item12 = value.Rest.Item5;
+ item13 = value.Rest.Item6;
+ item14 = value.Rest.Item7;
+ item15 = value.Rest.Rest.Item1;
+ item16 = value.Rest.Rest.Item2;
+ item17 = value.Rest.Rest.Item3;
+ item18 = value.Rest.Rest.Item4;
+ item19 = value.Rest.Rest.Item5;
+ item20 = value.Rest.Rest.Item6;
+ item21 = value.Rest.Rest.Item7;
+ }
+ #endregion
+
+ #region ToValueTuple
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 1 element.
+ /// </summary>
+ public static ValueTuple<T1>
+ ToValueTuple<T1>(
+ this Tuple<T1> value)
+ {
+ return ValueTuple.Create(value.Item1);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 2 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2>
+ ToValueTuple<T1, T2>(
+ this Tuple<T1, T2> value)
+ {
+ return ValueTuple.Create(value.Item1, value.Item2);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 3 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3>
+ ToValueTuple<T1, T2, T3>(
+ this Tuple<T1, T2, T3> value)
+ {
+ return ValueTuple.Create(value.Item1, value.Item2, value.Item3);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 4 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4>
+ ToValueTuple<T1, T2, T3, T4>(
+ this Tuple<T1, T2, T3, T4> value)
+ {
+ return ValueTuple.Create(value.Item1, value.Item2, value.Item3, value.Item4);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 5 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5>
+ ToValueTuple<T1, T2, T3, T4, T5>(
+ this Tuple<T1, T2, T3, T4, T5> value)
+ {
+ return ValueTuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 6 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6>
+ ToValueTuple<T1, T2, T3, T4, T5, T6>(
+ this Tuple<T1, T2, T3, T4, T5, T6> value)
+ {
+ return ValueTuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 7 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7> value)
+ {
+ return ValueTuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 8 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ ValueTuple.Create(value.Rest.Item1));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 9 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ ValueTuple.Create(value.Rest.Item1, value.Rest.Item2));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 10 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 11 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 12 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 13 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 14 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ ValueTuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 15 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15>>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15>>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ ValueTuple.Create(value.Rest.Rest.Item1)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 16 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16>>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16>>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 17 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17>>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17>>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 18 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17, T18>>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18>>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 19 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17, T18, T19>>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18, T19>>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 20 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17, T18, T19, T20>>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18, T19, T20>>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5, value.Rest.Rest.Item6)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="ValueTuple"/> from a properly nested <see cref="Tuple"/> with 21 elements.
+ /// </summary>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17, T18, T19, T20, T21>>>
+ ToValueTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>(
+ this Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18, T19, T20, T21>>> value)
+ {
+ return CreateLong(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLong(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ ValueTuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5, value.Rest.Rest.Item6, value.Rest.Rest.Item7)));
+ }
+ #endregion
+
+ #region ToTuple
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 1 element.
+ /// </summary>
+ public static Tuple<T1>
+ ToTuple<T1>(
+ this ValueTuple<T1> value)
+ {
+ return Tuple.Create(value.Item1);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 2 elements.
+ /// </summary>
+ public static Tuple<T1, T2>
+ ToTuple<T1, T2>(
+ this ValueTuple<T1, T2> value)
+ {
+ return Tuple.Create(value.Item1, value.Item2);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 3 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3>
+ ToTuple<T1, T2, T3>(
+ this ValueTuple<T1, T2, T3> value)
+ {
+ return Tuple.Create(value.Item1, value.Item2, value.Item3);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 4 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4>
+ ToTuple<T1, T2, T3, T4>(
+ this ValueTuple<T1, T2, T3, T4> value)
+ {
+ return Tuple.Create(value.Item1, value.Item2, value.Item3, value.Item4);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 5 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5>
+ ToTuple<T1, T2, T3, T4, T5>(
+ this ValueTuple<T1, T2, T3, T4, T5> value)
+ {
+ return Tuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 6 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6>
+ ToTuple<T1, T2, T3, T4, T5, T6>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6> value)
+ {
+ return Tuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 7 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7> value)
+ {
+ return Tuple.Create(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7);
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 8 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ Tuple.Create(value.Rest.Item1));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 9 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ Tuple.Create(value.Rest.Item1, value.Rest.Item2));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 10 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 11 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 12 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 13 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 14 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ Tuple.Create(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 15 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15>>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15>>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ Tuple.Create(value.Rest.Rest.Item1)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 16 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16>>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16>>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 17 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17>>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17>>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 18 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18>>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17, T18>>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 19 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18, T19>>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17, T18, T19>>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 20 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18, T19, T20>>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17, T18, T19, T20>>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5, value.Rest.Rest.Item6)));
+ }
+
+ /// <summary>
+ /// Make a properly nested <see cref="Tuple"/> from a properly nested <see cref="ValueTuple"/> with 21 elements.
+ /// </summary>
+ public static Tuple<T1, T2, T3, T4, T5, T6, T7, Tuple<T8, T9, T10, T11, T12, T13, T14, Tuple<T15, T16, T17, T18, T19, T20, T21>>>
+ ToTuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>(
+ this ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8, T9, T10, T11, T12, T13, T14, ValueTuple<T15, T16, T17, T18, T19, T20, T21>>> value)
+ {
+ return CreateLongRef(value.Item1, value.Item2, value.Item3, value.Item4, value.Item5, value.Item6, value.Item7,
+ CreateLongRef(value.Rest.Item1, value.Rest.Item2, value.Rest.Item3, value.Rest.Item4, value.Rest.Item5, value.Rest.Item6, value.Rest.Item7,
+ Tuple.Create(value.Rest.Rest.Item1, value.Rest.Rest.Item2, value.Rest.Rest.Item3, value.Rest.Rest.Item4, value.Rest.Rest.Item5, value.Rest.Rest.Item6, value.Rest.Rest.Item7)));
+ }
+ #endregion
+
+ private static ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> CreateLong<T1, T2, T3, T4, T5, T6, T7, TRest>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) where TRest : struct, ITuple =>
+ new ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>(item1, item2, item3, item4, item5, item6, item7, rest);
+
+ private static Tuple<T1, T2, T3, T4, T5, T6, T7, TRest> CreateLongRef<T1, T2, T3, T4, T5, T6, T7, TRest>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) where TRest : ITuple =>
+ new Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>(item1, item2, item3, item4, item5, item6, item7, rest);
+ }
+}
diff --git a/src/mscorlib/shared/System/Type.Enum.cs b/src/mscorlib/shared/System/Type.Enum.cs
new file mode 100644
index 0000000000..4d82410383
--- /dev/null
+++ b/src/mscorlib/shared/System/Type.Enum.cs
@@ -0,0 +1,186 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Reflection;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace System
+{
+ //
+ // This file collects a set of Enum-related apis that run when the Type is subclassed by an application.
+ // None of it runs on normal Type objects supplied by the runtime (as those types override these methods.)
+ //
+ // Since app-subclassed Types are "untrusted classes" that may or may not implement the complete surface area correctly,
+ // this code should be considered brittle and not changed lightly.
+ //
+ public abstract partial class Type
+ {
+ public virtual bool IsEnumDefined(object value)
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+
+ if (!IsEnum)
+ throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
+
+ // Check if both of them are of the same type
+ Type valueType = value.GetType();
+
+ // If the value is an Enum then we need to extract the underlying value from it
+ if (valueType.IsEnum)
+ {
+ if (!valueType.IsEquivalentTo(this))
+ throw new ArgumentException(SR.Format(SR.Arg_EnumAndObjectMustBeSameType, valueType.ToString(), this.ToString()));
+
+ valueType = valueType.GetEnumUnderlyingType();
+ }
+
+ // If a string is passed in
+ if (valueType == typeof(string))
+ {
+ string[] names = GetEnumNames();
+ if (Array.IndexOf(names, value) >= 0)
+ return true;
+ else
+ return false;
+ }
+
+ // If an enum or integer value is passed in
+ if (Type.IsIntegerType(valueType))
+ {
+ Type underlyingType = GetEnumUnderlyingType();
+ // We cannot compare the types directly because valueType is always a runtime type but underlyingType might not be.
+ if (underlyingType.GetTypeCodeImpl() != valueType.GetTypeCodeImpl())
+ throw new ArgumentException(SR.Format(SR.Arg_EnumUnderlyingTypeAndObjectMustBeSameType, valueType.ToString(), underlyingType.ToString()));
+
+ Array values = GetEnumRawConstantValues();
+ return (BinarySearch(values, value) >= 0);
+ }
+ else
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_UnknownEnumType);
+ }
+ }
+
+ public virtual string GetEnumName(object value)
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+
+ if (!IsEnum)
+ throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
+
+ Type valueType = value.GetType();
+
+ if (!(valueType.IsEnum || Type.IsIntegerType(valueType)))
+ throw new ArgumentException(SR.Arg_MustBeEnumBaseTypeOrEnum, nameof(value));
+
+ Array values = GetEnumRawConstantValues();
+ int index = BinarySearch(values, value);
+
+ if (index >= 0)
+ {
+ string[] names = GetEnumNames();
+ return names[index];
+ }
+
+ return null;
+ }
+
+ public virtual string[] GetEnumNames()
+ {
+ if (!IsEnum)
+ throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
+
+ string[] names;
+ Array values;
+ GetEnumData(out names, out values);
+ return names;
+ }
+
+
+ // Returns the enum values as an object array.
+ private Array GetEnumRawConstantValues()
+ {
+ string[] names;
+ Array values;
+ GetEnumData(out names, out values);
+ return values;
+ }
+
+ // This will return enumValues and enumNames sorted by the values.
+ private void GetEnumData(out string[] enumNames, out Array enumValues)
+ {
+ FieldInfo[] flds = GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
+
+ object[] values = new object[flds.Length];
+ string[] names = new string[flds.Length];
+
+ for (int i = 0; i < flds.Length; i++)
+ {
+ names[i] = flds[i].Name;
+ values[i] = flds[i].GetRawConstantValue();
+ }
+
+ // Insertion Sort these values in ascending order.
+ // We use this O(n^2) algorithm, but it turns out that most of the time the elements are already in sorted order and
+ // the common case performance will be faster than quick sorting this.
+ IComparer comparer = Comparer<object>.Default;
+ for (int i = 1; i < values.Length; i++)
+ {
+ int j = i;
+ string tempStr = names[i];
+ object val = values[i];
+ bool exchanged = false;
+
+ // Since the elements are sorted we only need to do one comparision, we keep the check for j inside the loop.
+ while (comparer.Compare(values[j - 1], val) > 0)
+ {
+ names[j] = names[j - 1];
+ values[j] = values[j - 1];
+ j--;
+ exchanged = true;
+ if (j == 0)
+ break;
+ }
+
+ if (exchanged)
+ {
+ names[j] = tempStr;
+ values[j] = val;
+ }
+ }
+
+ enumNames = names;
+ enumValues = values;
+ }
+
+ // Convert everything to ulong then perform a binary search.
+ private static int BinarySearch(Array array, object value)
+ {
+ ulong[] ulArray = new ulong[array.Length];
+ for (int i = 0; i < array.Length; ++i)
+ ulArray[i] = Enum.ToUInt64(array.GetValue(i));
+
+ ulong ulValue = Enum.ToUInt64(value);
+
+ return Array.BinarySearch(ulArray, ulValue);
+ }
+
+ internal static bool IsIntegerType(Type t)
+ {
+ return (t == typeof(int) ||
+ t == typeof(short) ||
+ t == typeof(ushort) ||
+ t == typeof(byte) ||
+ t == typeof(sbyte) ||
+ t == typeof(uint) ||
+ t == typeof(long) ||
+ t == typeof(ulong) ||
+ t == typeof(char) ||
+ t == typeof(bool));
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Type.Helpers.cs b/src/mscorlib/shared/System/Type.Helpers.cs
new file mode 100644
index 0000000000..db8df231e4
--- /dev/null
+++ b/src/mscorlib/shared/System/Type.Helpers.cs
@@ -0,0 +1,527 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Reflection;
+
+namespace System
+{
+ // This file collects the longer methods of Type to make the main Type class more readable.
+ public abstract partial class Type : MemberInfo, IReflect
+ {
+ public virtual bool IsSerializable
+ {
+ get
+ {
+ if ((GetAttributeFlagsImpl() & TypeAttributes.Serializable) != 0)
+ return true;
+
+ Type underlyingType = UnderlyingSystemType;
+ if (underlyingType.IsRuntimeImplemented())
+ {
+ do
+ {
+ // In all sane cases we only need to compare the direct level base type with
+ // System.Enum and System.MulticastDelegate. However, a generic parameter can
+ // have a base type constraint that is Delegate or even a real delegate type.
+ // Let's maintain compatibility and return true for them.
+ if (underlyingType == typeof(Delegate) || underlyingType == typeof(Enum))
+ return true;
+
+ underlyingType = underlyingType.BaseType;
+ }
+ while (underlyingType != null);
+ }
+
+ return false;
+ }
+ }
+
+ public virtual bool ContainsGenericParameters
+ {
+ get
+ {
+ if (HasElementType)
+ return GetRootElementType().ContainsGenericParameters;
+
+ if (IsGenericParameter)
+ return true;
+
+ if (!IsGenericType)
+ return false;
+
+ Type[] genericArguments = GetGenericArguments();
+ for (int i = 0; i < genericArguments.Length; i++)
+ {
+ if (genericArguments[i].ContainsGenericParameters)
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ internal Type GetRootElementType()
+ {
+ Type rootElementType = this;
+
+ while (rootElementType.HasElementType)
+ rootElementType = rootElementType.GetElementType();
+
+ return rootElementType;
+ }
+
+ public bool IsVisible
+ {
+ get
+ {
+#if CORECLR
+ RuntimeType rt = this as RuntimeType;
+ if (rt != null)
+ return RuntimeTypeHandle.IsVisible(rt);
+#endif //CORECLR
+
+ if (IsGenericParameter)
+ return true;
+
+ if (HasElementType)
+ return GetElementType().IsVisible;
+
+ Type type = this;
+ while (type.IsNested)
+ {
+ if (!type.IsNestedPublic)
+ return false;
+
+ // this should be null for non-nested types.
+ type = type.DeclaringType;
+ }
+
+ // Now "type" should be a top level type
+ if (!type.IsPublic)
+ return false;
+
+ if (IsGenericType && !IsGenericTypeDefinition)
+ {
+ foreach (Type t in GetGenericArguments())
+ {
+ if (!t.IsVisible)
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ public virtual Type[] FindInterfaces(TypeFilter filter, object filterCriteria)
+ {
+ if (filter == null)
+ throw new ArgumentNullException(nameof(filter));
+
+ Type[] c = GetInterfaces();
+ int cnt = 0;
+ for (int i = 0; i < c.Length; i++)
+ {
+ if (!filter(c[i], filterCriteria))
+ c[i] = null;
+ else
+ cnt++;
+ }
+ if (cnt == c.Length)
+ return c;
+
+ Type[] ret = new Type[cnt];
+ cnt = 0;
+ for (int i = 0; i < c.Length; i++)
+ {
+ if (c[i] != null)
+ ret[cnt++] = c[i];
+ }
+ return ret;
+ }
+
+ public virtual MemberInfo[] FindMembers(MemberTypes memberType, BindingFlags bindingAttr, MemberFilter filter, object filterCriteria)
+ {
+ // Define the work arrays
+ MethodInfo[] m = null;
+ ConstructorInfo[] c = null;
+ FieldInfo[] f = null;
+ PropertyInfo[] p = null;
+ EventInfo[] e = null;
+ Type[] t = null;
+
+ int i = 0;
+ int cnt = 0; // Total Matchs
+
+ // Check the methods
+ if ((memberType & MemberTypes.Method) != 0)
+ {
+ m = GetMethods(bindingAttr);
+ if (filter != null)
+ {
+ for (i = 0; i < m.Length; i++)
+ if (!filter(m[i], filterCriteria))
+ m[i] = null;
+ else
+ cnt++;
+ }
+ else
+ {
+ cnt += m.Length;
+ }
+ }
+
+ // Check the constructors
+ if ((memberType & MemberTypes.Constructor) != 0)
+ {
+ c = GetConstructors(bindingAttr);
+ if (filter != null)
+ {
+ for (i = 0; i < c.Length; i++)
+ if (!filter(c[i], filterCriteria))
+ c[i] = null;
+ else
+ cnt++;
+ }
+ else
+ {
+ cnt += c.Length;
+ }
+ }
+
+ // Check the fields
+ if ((memberType & MemberTypes.Field) != 0)
+ {
+ f = GetFields(bindingAttr);
+ if (filter != null)
+ {
+ for (i = 0; i < f.Length; i++)
+ if (!filter(f[i], filterCriteria))
+ f[i] = null;
+ else
+ cnt++;
+ }
+ else
+ {
+ cnt += f.Length;
+ }
+ }
+
+ // Check the Properties
+ if ((memberType & MemberTypes.Property) != 0)
+ {
+ p = GetProperties(bindingAttr);
+ if (filter != null)
+ {
+ for (i = 0; i < p.Length; i++)
+ if (!filter(p[i], filterCriteria))
+ p[i] = null;
+ else
+ cnt++;
+ }
+ else
+ {
+ cnt += p.Length;
+ }
+ }
+
+ // Check the Events
+ if ((memberType & MemberTypes.Event) != 0)
+ {
+ e = GetEvents(bindingAttr);
+ if (filter != null)
+ {
+ for (i = 0; i < e.Length; i++)
+ if (!filter(e[i], filterCriteria))
+ e[i] = null;
+ else
+ cnt++;
+ }
+ else
+ {
+ cnt += e.Length;
+ }
+ }
+
+ // Check the Types
+ if ((memberType & MemberTypes.NestedType) != 0)
+ {
+ t = GetNestedTypes(bindingAttr);
+ if (filter != null)
+ {
+ for (i = 0; i < t.Length; i++)
+ if (!filter(t[i], filterCriteria))
+ t[i] = null;
+ else
+ cnt++;
+ }
+ else
+ {
+ cnt += t.Length;
+ }
+ }
+
+ // Allocate the Member Info
+ MemberInfo[] ret = new MemberInfo[cnt];
+
+ // Copy the Methods
+ cnt = 0;
+ if (m != null)
+ {
+ for (i = 0; i < m.Length; i++)
+ if (m[i] != null)
+ ret[cnt++] = m[i];
+ }
+
+ // Copy the Constructors
+ if (c != null)
+ {
+ for (i = 0; i < c.Length; i++)
+ if (c[i] != null)
+ ret[cnt++] = c[i];
+ }
+
+ // Copy the Fields
+ if (f != null)
+ {
+ for (i = 0; i < f.Length; i++)
+ if (f[i] != null)
+ ret[cnt++] = f[i];
+ }
+
+ // Copy the Properties
+ if (p != null)
+ {
+ for (i = 0; i < p.Length; i++)
+ if (p[i] != null)
+ ret[cnt++] = p[i];
+ }
+
+ // Copy the Events
+ if (e != null)
+ {
+ for (i = 0; i < e.Length; i++)
+ if (e[i] != null)
+ ret[cnt++] = e[i];
+ }
+
+ // Copy the Types
+ if (t != null)
+ {
+ for (i = 0; i < t.Length; i++)
+ if (t[i] != null)
+ ret[cnt++] = t[i];
+ }
+
+ return ret;
+ }
+
+ public virtual bool IsSubclassOf(Type c)
+ {
+ Type p = this;
+ if (p == c)
+ return false;
+ while (p != null)
+ {
+ if (p == c)
+ return true;
+ p = p.BaseType;
+ }
+ return false;
+ }
+
+ public virtual bool IsAssignableFrom(Type c)
+ {
+ if (c == null)
+ return false;
+
+ if (this == c)
+ return true;
+
+ // For backward-compatibility, we need to special case for the types
+ // whose UnderlyingSystemType are runtime implemented.
+ Type toType = this.UnderlyingSystemType;
+ if (toType.IsRuntimeImplemented())
+ return toType.IsAssignableFrom(c);
+
+ // If c is a subclass of this class, then c can be cast to this type.
+ if (c.IsSubclassOf(this))
+ return true;
+
+ if (this.IsInterface)
+ {
+ return c.ImplementInterface(this);
+ }
+ else if (IsGenericParameter)
+ {
+ Type[] constraints = GetGenericParameterConstraints();
+ for (int i = 0; i < constraints.Length; i++)
+ if (!constraints[i].IsAssignableFrom(c))
+ return false;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ internal bool ImplementInterface(Type ifaceType)
+ {
+ Type t = this;
+ while (t != null)
+ {
+ Type[] interfaces = t.GetInterfaces();
+ if (interfaces != null)
+ {
+ for (int i = 0; i < interfaces.Length; i++)
+ {
+ // Interfaces don't derive from other interfaces, they implement them.
+ // So instead of IsSubclassOf, we should use ImplementInterface instead.
+ if (interfaces[i] == ifaceType ||
+ (interfaces[i] != null && interfaces[i].ImplementInterface(ifaceType)))
+ return true;
+ }
+ }
+
+ t = t.BaseType;
+ }
+
+ return false;
+ }
+
+ // FilterAttribute
+ // This method will search for a member based upon the attribute passed in.
+ // filterCriteria -- an Int32 representing the attribute
+ private static bool FilterAttributeImpl(MemberInfo m, object filterCriteria)
+ {
+ // Check that the criteria object is an Integer object
+ if (filterCriteria == null)
+ throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritInt);
+
+ switch (m.MemberType)
+ {
+ case MemberTypes.Constructor:
+ case MemberTypes.Method:
+ {
+ MethodAttributes criteria = 0;
+ try
+ {
+ int i = (int)filterCriteria;
+ criteria = (MethodAttributes)i;
+ }
+ catch
+ {
+ throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritInt);
+ }
+
+
+ MethodAttributes attr;
+ if (m.MemberType == MemberTypes.Method)
+ attr = ((MethodInfo)m).Attributes;
+ else
+ attr = ((ConstructorInfo)m).Attributes;
+
+ if (((criteria & MethodAttributes.MemberAccessMask) != 0) && (attr & MethodAttributes.MemberAccessMask) != (criteria & MethodAttributes.MemberAccessMask))
+ return false;
+ if (((criteria & MethodAttributes.Static) != 0) && (attr & MethodAttributes.Static) == 0)
+ return false;
+ if (((criteria & MethodAttributes.Final) != 0) && (attr & MethodAttributes.Final) == 0)
+ return false;
+ if (((criteria & MethodAttributes.Virtual) != 0) && (attr & MethodAttributes.Virtual) == 0)
+ return false;
+ if (((criteria & MethodAttributes.Abstract) != 0) && (attr & MethodAttributes.Abstract) == 0)
+ return false;
+ if (((criteria & MethodAttributes.SpecialName) != 0) && (attr & MethodAttributes.SpecialName) == 0)
+ return false;
+ return true;
+ }
+ case MemberTypes.Field:
+ {
+ FieldAttributes criteria = 0;
+ try
+ {
+ int i = (int)filterCriteria;
+ criteria = (FieldAttributes)i;
+ }
+ catch
+ {
+ throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritInt);
+ }
+
+ FieldAttributes attr = ((FieldInfo)m).Attributes;
+ if (((criteria & FieldAttributes.FieldAccessMask) != 0) && (attr & FieldAttributes.FieldAccessMask) != (criteria & FieldAttributes.FieldAccessMask))
+ return false;
+ if (((criteria & FieldAttributes.Static) != 0) && (attr & FieldAttributes.Static) == 0)
+ return false;
+ if (((criteria & FieldAttributes.InitOnly) != 0) && (attr & FieldAttributes.InitOnly) == 0)
+ return false;
+ if (((criteria & FieldAttributes.Literal) != 0) && (attr & FieldAttributes.Literal) == 0)
+ return false;
+ if (((criteria & FieldAttributes.NotSerialized) != 0) && (attr & FieldAttributes.NotSerialized) == 0)
+ return false;
+ if (((criteria & FieldAttributes.PinvokeImpl) != 0) && (attr & FieldAttributes.PinvokeImpl) == 0)
+ return false;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // FilterName
+ // This method will filter based upon the name. A partial wildcard
+ // at the end of the string is supported.
+ // filterCriteria -- This is the string name
+ private static bool FilterNameImpl(MemberInfo m, object filterCriteria)
+ {
+ // Check that the criteria object is a String object
+ if (filterCriteria == null || !(filterCriteria is string))
+ throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString);
+
+ // At the moment this fails if its done on a single line....
+ string str = ((string)filterCriteria);
+ str = str.Trim();
+
+ string name = m.Name;
+ // Get the nested class name only, as opposed to the mangled one
+ if (m.MemberType == MemberTypes.NestedType)
+ name = name.Substring(name.LastIndexOf('+') + 1);
+ // Check to see if this is a prefix or exact match requirement
+ if (str.Length > 0 && str[str.Length - 1] == '*')
+ {
+ str = str.Substring(0, str.Length - 1);
+ return (name.StartsWith(str, StringComparison.Ordinal));
+ }
+
+ return (name.Equals(str));
+ }
+
+ // FilterIgnoreCase
+ // This delegate will do a name search but does it with the
+ // ignore case specified.
+ private static bool FilterNameIgnoreCaseImpl(MemberInfo m, object filterCriteria)
+ {
+ // Check that the criteria object is a String object
+ if (filterCriteria == null || !(filterCriteria is string))
+ throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString);
+
+ string str = (string)filterCriteria;
+ str = str.Trim();
+
+ string name = m.Name;
+ // Get the nested class name only, as opposed to the mangled one
+ if (m.MemberType == MemberTypes.NestedType)
+ name = name.Substring(name.LastIndexOf('+') + 1);
+ // Check to see if this is a prefix or exact match requirement
+ if (str.Length > 0 && str[str.Length - 1] == '*')
+ {
+ str = str.Substring(0, str.Length - 1);
+ return (string.Compare(name, 0, str, 0, str.Length, StringComparison.OrdinalIgnoreCase) == 0);
+ }
+
+ return (string.Compare(str, name, StringComparison.OrdinalIgnoreCase) == 0);
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/Type.cs b/src/mscorlib/shared/System/Type.cs
new file mode 100644
index 0000000000..7749c17414
--- /dev/null
+++ b/src/mscorlib/shared/System/Type.cs
@@ -0,0 +1,358 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Threading;
+using System.Reflection;
+using System.Diagnostics;
+using System.Globalization;
+using System.Runtime.InteropServices;
+
+namespace System
+{
+ public abstract partial class Type : MemberInfo, IReflect
+ {
+ protected Type() { }
+
+ public override MemberTypes MemberType => MemberTypes.TypeInfo;
+
+ public new Type GetType() => base.GetType();
+
+ public abstract string Namespace { get; }
+ public abstract string AssemblyQualifiedName { get; }
+ public abstract string FullName { get; }
+
+ public abstract Assembly Assembly { get; }
+ public abstract new Module Module { get; }
+
+ public bool IsNested => DeclaringType != null;
+ public override Type DeclaringType => null;
+ public virtual MethodBase DeclaringMethod => null;
+
+ public override Type ReflectedType => null;
+ public abstract Type UnderlyingSystemType { get; }
+
+ public bool IsArray => IsArrayImpl();
+ protected abstract bool IsArrayImpl();
+ public bool IsByRef => IsByRefImpl();
+ protected abstract bool IsByRefImpl();
+ public bool IsPointer => IsPointerImpl();
+ protected abstract bool IsPointerImpl();
+ public virtual bool IsConstructedGenericType { get { throw NotImplemented.ByDesign; } }
+ public virtual bool IsGenericParameter => false;
+ public virtual bool IsGenericType => false;
+ public virtual bool IsGenericTypeDefinition => false;
+
+ public virtual bool IsSZArray { get { throw NotImplemented.ByDesign; } }
+ public virtual bool IsVariableBoundArray => IsArray && !IsSZArray;
+
+ public bool HasElementType => HasElementTypeImpl();
+ protected abstract bool HasElementTypeImpl();
+ public abstract Type GetElementType();
+
+ public virtual int GetArrayRank() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+
+ public virtual Type GetGenericTypeDefinition() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+ public virtual Type[] GenericTypeArguments => (IsGenericType && !IsGenericTypeDefinition) ? GetGenericArguments() : Array.Empty<Type>();
+ public virtual Type[] GetGenericArguments() { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+
+ public virtual int GenericParameterPosition { get { throw new InvalidOperationException(SR.Arg_NotGenericParameter); } }
+ public virtual GenericParameterAttributes GenericParameterAttributes { get { throw new NotSupportedException(); } }
+ public virtual Type[] GetGenericParameterConstraints()
+ {
+ if (!IsGenericParameter)
+ throw new InvalidOperationException(SR.Arg_NotGenericParameter);
+ throw new InvalidOperationException();
+ }
+
+ public TypeAttributes Attributes => GetAttributeFlagsImpl();
+ protected abstract TypeAttributes GetAttributeFlagsImpl();
+
+ public bool IsAbstract => (GetAttributeFlagsImpl() & TypeAttributes.Abstract) != 0;
+ public bool IsImport => (GetAttributeFlagsImpl() & TypeAttributes.Import) != 0;
+ public bool IsSealed => (GetAttributeFlagsImpl() & TypeAttributes.Sealed) != 0;
+ public bool IsSpecialName => (GetAttributeFlagsImpl() & TypeAttributes.SpecialName) != 0;
+
+ public bool IsClass => (GetAttributeFlagsImpl() & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class && !IsValueType;
+
+ public bool IsNestedAssembly => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedAssembly;
+ public bool IsNestedFamANDAssem => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamANDAssem;
+ public bool IsNestedFamily => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamily;
+ public bool IsNestedFamORAssem => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedFamORAssem;
+ public bool IsNestedPrivate => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPrivate;
+ public bool IsNestedPublic => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NestedPublic;
+ public bool IsNotPublic => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic;
+ public bool IsPublic => (GetAttributeFlagsImpl() & TypeAttributes.VisibilityMask) == TypeAttributes.Public;
+
+ public bool IsAutoLayout => (GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.AutoLayout;
+ public bool IsExplicitLayout => (GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.ExplicitLayout;
+ public bool IsLayoutSequential => (GetAttributeFlagsImpl() & TypeAttributes.LayoutMask) == TypeAttributes.SequentialLayout;
+
+ public bool IsAnsiClass => (GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.AnsiClass;
+ public bool IsAutoClass => (GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.AutoClass;
+ public bool IsUnicodeClass => (GetAttributeFlagsImpl() & TypeAttributes.StringFormatMask) == TypeAttributes.UnicodeClass;
+
+ public bool IsCOMObject => IsCOMObjectImpl();
+ protected abstract bool IsCOMObjectImpl();
+ public bool IsContextful => IsContextfulImpl();
+ protected virtual bool IsContextfulImpl() => false;
+
+ public virtual bool IsEnum => IsSubclassOf(typeof(Enum));
+ public bool IsMarshalByRef => IsMarshalByRefImpl();
+ protected virtual bool IsMarshalByRefImpl() => false;
+ public bool IsPrimitive => IsPrimitiveImpl();
+ protected abstract bool IsPrimitiveImpl();
+ public bool IsValueType => IsValueTypeImpl();
+ protected virtual bool IsValueTypeImpl() => IsSubclassOf(typeof(ValueType));
+
+ public virtual bool IsSecurityCritical { get { throw NotImplemented.ByDesign; } }
+ public virtual bool IsSecuritySafeCritical { get { throw NotImplemented.ByDesign; } }
+ public virtual bool IsSecurityTransparent { get { throw NotImplemented.ByDesign; } }
+
+ public virtual StructLayoutAttribute StructLayoutAttribute { get { throw new NotSupportedException(); } }
+ public ConstructorInfo TypeInitializer => GetConstructorImpl(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any, Type.EmptyTypes, null);
+
+ public ConstructorInfo GetConstructor(Type[] types) => GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, types, null);
+ public ConstructorInfo GetConstructor(BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => GetConstructor(bindingAttr, binder, CallingConventions.Any, types, modifiers);
+ public ConstructorInfo GetConstructor(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (types == null)
+ throw new ArgumentNullException(nameof(types));
+ for (int i = 0; i < types.Length; i++)
+ {
+ if (types[i] == null)
+ throw new ArgumentNullException(nameof(types));
+ }
+ return GetConstructorImpl(bindingAttr, binder, callConvention, types, modifiers);
+ }
+ protected abstract ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers);
+
+ public ConstructorInfo[] GetConstructors() => GetConstructors(BindingFlags.Public | BindingFlags.Instance);
+ public abstract ConstructorInfo[] GetConstructors(BindingFlags bindingAttr);
+
+ public EventInfo GetEvent(string name) => GetEvent(name, Type.DefaultLookup);
+ public abstract EventInfo GetEvent(string name, BindingFlags bindingAttr);
+
+ public virtual EventInfo[] GetEvents() => GetEvents(Type.DefaultLookup);
+ public abstract EventInfo[] GetEvents(BindingFlags bindingAttr);
+
+ public FieldInfo GetField(string name) => GetField(name, Type.DefaultLookup);
+ public abstract FieldInfo GetField(string name, BindingFlags bindingAttr);
+
+ public FieldInfo[] GetFields() => GetFields(Type.DefaultLookup);
+ public abstract FieldInfo[] GetFields(BindingFlags bindingAttr);
+
+ public MemberInfo[] GetMember(string name) => GetMember(name, Type.DefaultLookup);
+ public virtual MemberInfo[] GetMember(string name, BindingFlags bindingAttr) => GetMember(name, MemberTypes.All, bindingAttr);
+ public virtual MemberInfo[] GetMember(string name, MemberTypes type, BindingFlags bindingAttr) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+
+ public MemberInfo[] GetMembers() => GetMembers(Type.DefaultLookup);
+ public abstract MemberInfo[] GetMembers(BindingFlags bindingAttr);
+
+ public MethodInfo GetMethod(string name) => GetMethod(name, Type.DefaultLookup);
+ public MethodInfo GetMethod(string name, BindingFlags bindingAttr)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+ return GetMethodImpl(name, bindingAttr, null, CallingConventions.Any, null, null);
+ }
+
+ public MethodInfo GetMethod(string name, Type[] types) => GetMethod(name, types, null);
+ public MethodInfo GetMethod(string name, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, Type.DefaultLookup, null, types, modifiers);
+ public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => GetMethod(name, bindingAttr, binder, CallingConventions.Any, types, modifiers);
+ public MethodInfo GetMethod(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+ if (types == null)
+ throw new ArgumentNullException(nameof(types));
+ for (int i = 0; i < types.Length; i++)
+ {
+ if (types[i] == null)
+ throw new ArgumentNullException(nameof(types));
+ }
+ return GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers);
+ }
+
+ protected abstract MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers);
+
+ public MethodInfo[] GetMethods() => GetMethods(Type.DefaultLookup);
+ public abstract MethodInfo[] GetMethods(BindingFlags bindingAttr);
+
+ public Type GetNestedType(string name) => GetNestedType(name, Type.DefaultLookup);
+ public abstract Type GetNestedType(string name, BindingFlags bindingAttr);
+
+ public Type[] GetNestedTypes() => GetNestedTypes(Type.DefaultLookup);
+ public abstract Type[] GetNestedTypes(BindingFlags bindingAttr);
+
+ public PropertyInfo GetProperty(string name) => GetProperty(name, Type.DefaultLookup);
+ public PropertyInfo GetProperty(string name, BindingFlags bindingAttr)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+ return GetPropertyImpl(name, bindingAttr, null, null, null, null);
+ }
+
+ public PropertyInfo GetProperty(string name, Type returnType)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+ if (returnType == null)
+ throw new ArgumentNullException(nameof(returnType));
+ return GetPropertyImpl(name, Type.DefaultLookup, null, returnType, null, null);
+ }
+
+ public PropertyInfo GetProperty(string name, Type[] types) => GetProperty(name, null, types);
+ public PropertyInfo GetProperty(string name, Type returnType, Type[] types) => GetProperty(name, returnType, types, null);
+ public PropertyInfo GetProperty(string name, Type returnType, Type[] types, ParameterModifier[] modifiers) => GetProperty(name, Type.DefaultLookup, null, returnType, types, modifiers);
+ public PropertyInfo GetProperty(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
+ {
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+ if (types == null)
+ throw new ArgumentNullException(nameof(types));
+ return GetPropertyImpl(name, bindingAttr, binder, returnType, types, modifiers);
+ }
+
+ protected abstract PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers);
+
+ public PropertyInfo[] GetProperties() => GetProperties(Type.DefaultLookup);
+ public abstract PropertyInfo[] GetProperties(BindingFlags bindingAttr);
+
+ public virtual MemberInfo[] GetDefaultMembers() { throw NotImplemented.ByDesign; }
+
+ public virtual RuntimeTypeHandle TypeHandle { get { throw new NotSupportedException(); } }
+ public static RuntimeTypeHandle GetTypeHandle(object o)
+ {
+ if (o == null)
+ throw new ArgumentNullException(null, SR.Arg_InvalidHandle);
+ Type type = o.GetType();
+ return type.TypeHandle;
+ }
+
+ public static Type[] GetTypeArray(object[] args)
+ {
+ if (args == null)
+ throw new ArgumentNullException(nameof(args));
+
+ Type[] cls = new Type[args.Length];
+ for (int i = 0; i < cls.Length; i++)
+ {
+ if (args[i] == null)
+ throw new ArgumentNullException();
+ cls[i] = args[i].GetType();
+ }
+ return cls;
+ }
+
+ public static TypeCode GetTypeCode(Type type)
+ {
+ if (type == null)
+ return TypeCode.Empty;
+ return type.GetTypeCodeImpl();
+ }
+ protected virtual TypeCode GetTypeCodeImpl()
+ {
+ if (this != UnderlyingSystemType && UnderlyingSystemType != null)
+ return Type.GetTypeCode(UnderlyingSystemType);
+
+ return TypeCode.Object;
+ }
+
+ public abstract Guid GUID { get; }
+
+ public static Type GetTypeFromCLSID(Guid clsid) => GetTypeFromCLSID(clsid, null, throwOnError: false);
+ public static Type GetTypeFromCLSID(Guid clsid, bool throwOnError) => GetTypeFromCLSID(clsid, null, throwOnError: throwOnError);
+ public static Type GetTypeFromCLSID(Guid clsid, string server) => GetTypeFromCLSID(clsid, server, throwOnError: false);
+
+ public static Type GetTypeFromProgID(string progID) => GetTypeFromProgID(progID, null, throwOnError: false);
+ public static Type GetTypeFromProgID(string progID, bool throwOnError) => GetTypeFromProgID(progID, null, throwOnError: throwOnError);
+ public static Type GetTypeFromProgID(string progID, string server) => GetTypeFromProgID(progID, server, throwOnError: false);
+
+ public abstract Type BaseType { get; }
+
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args) => InvokeMember(name, invokeAttr, binder, target, args, null, null, null);
+
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, CultureInfo culture) => InvokeMember(name, invokeAttr, binder, target, args, null, culture, null);
+ public abstract object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters);
+
+ public Type GetInterface(string name) => GetInterface(name, ignoreCase: false);
+ public abstract Type GetInterface(string name, bool ignoreCase);
+ public abstract Type[] GetInterfaces();
+
+ public virtual InterfaceMapping GetInterfaceMap(Type interfaceType) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+
+ public virtual bool IsInstanceOfType(object o) => o == null ? false : IsAssignableFrom(o.GetType());
+ public virtual bool IsEquivalentTo(Type other) => this == other;
+
+ public virtual Type GetEnumUnderlyingType()
+ {
+ if (!IsEnum)
+ throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
+
+ FieldInfo[] fields = GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+ if (fields == null || fields.Length != 1)
+ throw new ArgumentException(SR.Argument_InvalidEnum, "enumType");
+
+ return fields[0].FieldType;
+ }
+ public virtual Array GetEnumValues()
+ {
+ if (!IsEnum)
+ throw new ArgumentException(SR.Arg_MustBeEnum, "enumType");
+
+ // We don't support GetEnumValues in the default implementation because we cannot create an array of
+ // a non-runtime type. If there is strong need we can consider returning an object or int64 array.
+ throw NotImplemented.ByDesign;
+ }
+
+ public virtual Type MakeArrayType() { throw new NotSupportedException(); }
+ public virtual Type MakeArrayType(int rank) { throw new NotSupportedException(); }
+ public virtual Type MakeByRefType() { throw new NotSupportedException(); }
+ public virtual Type MakeGenericType(params Type[] typeArguments) { throw new NotSupportedException(SR.NotSupported_SubclassOverride); }
+ public virtual Type MakePointerType() { throw new NotSupportedException(); }
+
+ public override string ToString() => "Type: " + Name; // Why do we add the "Type: " prefix?
+
+ public override bool Equals(object o) => o == null ? false : Equals(o as Type);
+ public override int GetHashCode()
+ {
+ Type systemType = UnderlyingSystemType;
+ if (!object.ReferenceEquals(systemType, this))
+ return systemType.GetHashCode();
+ return base.GetHashCode();
+ }
+ public virtual bool Equals(Type o) => o == null ? false : object.ReferenceEquals(this.UnderlyingSystemType, o.UnderlyingSystemType);
+
+ public static Type ReflectionOnlyGetType(string typeName, bool throwIfNotFound, bool ignoreCase) { throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionOnly); }
+
+ public static Binder DefaultBinder
+ {
+ get
+ {
+ if (s_defaultBinder == null)
+ {
+ DefaultBinder binder = new DefaultBinder();
+ Interlocked.CompareExchange<Binder>(ref s_defaultBinder, binder, null);
+ }
+ return s_defaultBinder;
+ }
+ }
+
+ private static volatile Binder s_defaultBinder;
+
+ public static readonly char Delimiter = '.';
+ public static readonly Type[] EmptyTypes = Array.Empty<Type>();
+ public static readonly object Missing = System.Reflection.Missing.Value;
+
+ public static readonly MemberFilter FilterAttribute = FilterAttributeImpl;
+ public static readonly MemberFilter FilterName = FilterNameImpl;
+ public static readonly MemberFilter FilterNameIgnoreCase = FilterNameIgnoreCaseImpl;
+
+ private const BindingFlags DefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
+ }
+}
diff --git a/src/mscorlib/shared/System/TypeAccessException.cs b/src/mscorlib/shared/System/TypeAccessException.cs
new file mode 100644
index 0000000000..32afbdfeb8
--- /dev/null
+++ b/src/mscorlib/shared/System/TypeAccessException.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System
+{
+ // TypeAccessException derives from TypeLoadException rather than MemberAccessException because in
+ // pre-v4 releases of the runtime TypeLoadException was used in lieu of a TypeAccessException.
+ [Serializable]
+ public class TypeAccessException : TypeLoadException
+ {
+ public TypeAccessException()
+ : base(SR.Arg_TypeAccessException)
+ {
+ HResult = __HResults.COR_E_TYPEACCESS;
+ }
+
+ public TypeAccessException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_TYPEACCESS;
+ }
+
+ public TypeAccessException(string message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_TYPEACCESS;
+ }
+
+ protected TypeAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/TypeCode.cs b/src/mscorlib/shared/System/TypeCode.cs
new file mode 100644
index 0000000000..293eb1f1aa
--- /dev/null
+++ b/src/mscorlib/shared/System/TypeCode.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// The TypeCode enum represents the type code of an object. To obtain the
+// TypeCode for a given object, use the Value.GetTypeCode() method. The
+// TypeCode.Empty value represents a null object reference. The TypeCode.Object
+// value represents an object that doesn't implement the IConvertible interface. The
+// TypeCode.DBNull value represents the database null, which is distinct and
+// different from a null reference. The other type codes represent values that
+// use the given simple type encoding.
+//
+// Note that when an object has a given TypeCode, there is no guarantee that
+// the object is an instance of the corresponding System.XXX value class. For
+// example, an object with the type code TypeCode.Int32 might actually be an
+// instance of a nullable 32-bit integer type (with a value that isn't the
+// database null).
+//
+// There are no type codes for "Missing", "Error", "IDispatch", and "IUnknown".
+// These types of values are instead represented as classes. When the type code
+// of an object is TypeCode.Object, a further instance-of check can be used to
+// determine if the object is one of these values.
+
+namespace System
+{
+ [Serializable]
+ public enum TypeCode
+ {
+ Empty = 0, // Null reference
+ Object = 1, // Instance that isn't a value
+ DBNull = 2, // Database null value
+ Boolean = 3, // Boolean
+ Char = 4, // Unicode character
+ SByte = 5, // Signed 8-bit integer
+ Byte = 6, // Unsigned 8-bit integer
+ Int16 = 7, // Signed 16-bit integer
+ UInt16 = 8, // Unsigned 16-bit integer
+ Int32 = 9, // Signed 32-bit integer
+ UInt32 = 10, // Unsigned 32-bit integer
+ Int64 = 11, // Signed 64-bit integer
+ UInt64 = 12, // Unsigned 64-bit integer
+ Single = 13, // IEEE 32-bit float
+ Double = 14, // IEEE 64-bit double
+ Decimal = 15, // Decimal
+ DateTime = 16, // DateTime
+ String = 18, // Unicode character string
+ }
+}
diff --git a/src/mscorlib/shared/System/TypeInitializationException.cs b/src/mscorlib/shared/System/TypeInitializationException.cs
new file mode 100644
index 0000000000..9191028346
--- /dev/null
+++ b/src/mscorlib/shared/System/TypeInitializationException.cs
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*=============================================================================
+**
+**
+**
+** Purpose: The exception class to wrap exceptions thrown by
+** a type's class initializer (.cctor). This is sufficiently
+** distinct from a TypeLoadException, which means we couldn't
+** find the type.
+**
+**
+=============================================================================*/
+
+using System.Globalization;
+using System.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public sealed class TypeInitializationException : SystemException
+ {
+ private String _typeName;
+
+ // This exception is not creatable without specifying the
+ // inner exception.
+ private TypeInitializationException()
+ : base(SR.TypeInitialization_Default)
+ {
+ HResult = __HResults.COR_E_TYPEINITIALIZATION;
+ }
+
+
+ public TypeInitializationException(String fullTypeName, Exception innerException)
+ : this(fullTypeName, SR.Format(SR.TypeInitialization_Type, fullTypeName), innerException)
+ {
+ }
+
+ // This is called from within the runtime. I believe this is necessary
+ // for Interop only, though it's not particularly useful.
+ internal TypeInitializationException(String message) : base(message)
+ {
+ HResult = __HResults.COR_E_TYPEINITIALIZATION;
+ }
+
+ internal TypeInitializationException(String fullTypeName, String message, Exception innerException)
+ : base(message, innerException)
+ {
+ _typeName = fullTypeName;
+ HResult = __HResults.COR_E_TYPEINITIALIZATION;
+ }
+
+ internal TypeInitializationException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ _typeName = info.GetString("TypeName");
+ }
+
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData(info, context);
+ info.AddValue("TypeName", TypeName, typeof(String));
+ }
+
+ public String TypeName
+ {
+ get
+ {
+ if (_typeName == null)
+ {
+ return String.Empty;
+ }
+ return _typeName;
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/TypeUnloadedException.cs b/src/mscorlib/shared/System/TypeUnloadedException.cs
new file mode 100644
index 0000000000..33e4687772
--- /dev/null
+++ b/src/mscorlib/shared/System/TypeUnloadedException.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+
+namespace System
+{
+ [Serializable]
+ public class TypeUnloadedException : SystemException
+ {
+ public TypeUnloadedException()
+ : base(SR.Arg_TypeUnloadedException)
+ {
+ HResult = __HResults.COR_E_TYPEUNLOADED;
+ }
+
+ public TypeUnloadedException(string message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_TYPEUNLOADED;
+ }
+
+ public TypeUnloadedException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ HResult = __HResults.COR_E_TYPEUNLOADED;
+ }
+
+ //
+ // This constructor is required for serialization;
+ //
+ protected TypeUnloadedException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
+
diff --git a/src/mscorlib/shared/System/UnauthorizedAccessException.cs b/src/mscorlib/shared/System/UnauthorizedAccessException.cs
new file mode 100644
index 0000000000..997358826f
--- /dev/null
+++ b/src/mscorlib/shared/System/UnauthorizedAccessException.cs
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*============================================================
+**
+**
+**
+** Purpose: An exception for OS 'access denied' types of
+** errors, including IO and limited security types
+** of errors.
+**
+**
+===========================================================*/
+
+using System.Runtime.Serialization;
+
+namespace System
+{
+ // The UnauthorizedAccessException is thrown when access errors
+ // occur from IO or other OS methods.
+ [Serializable]
+ public class UnauthorizedAccessException : SystemException
+ {
+ public UnauthorizedAccessException()
+ : base(SR.Arg_UnauthorizedAccessException)
+ {
+ HResult = __HResults.COR_E_UNAUTHORIZEDACCESS;
+ }
+
+ public UnauthorizedAccessException(String message)
+ : base(message)
+ {
+ HResult = __HResults.COR_E_UNAUTHORIZEDACCESS;
+ }
+
+ public UnauthorizedAccessException(String message, Exception inner)
+ : base(message, inner)
+ {
+ HResult = __HResults.COR_E_UNAUTHORIZEDACCESS;
+ }
+
+ protected UnauthorizedAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/src/mscorlib/shared/System/UnhandledExceptionEventArgs.cs b/src/mscorlib/shared/System/UnhandledExceptionEventArgs.cs
new file mode 100644
index 0000000000..d33830181c
--- /dev/null
+++ b/src/mscorlib/shared/System/UnhandledExceptionEventArgs.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ [Serializable]
+ public class UnhandledExceptionEventArgs : EventArgs
+ {
+ private Object _exception;
+ private bool _isTerminating;
+
+ public UnhandledExceptionEventArgs(Object exception, bool isTerminating)
+ {
+ _exception = exception;
+ _isTerminating = isTerminating;
+ }
+
+ public Object ExceptionObject
+ {
+ get { return _exception; }
+ }
+
+ public bool IsTerminating
+ {
+ get { return _isTerminating; }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/UnhandledExceptionEventHandler.cs b/src/mscorlib/shared/System/UnhandledExceptionEventHandler.cs
new file mode 100644
index 0000000000..b99414c189
--- /dev/null
+++ b/src/mscorlib/shared/System/UnhandledExceptionEventHandler.cs
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System
+{
+ [Serializable]
+ public delegate void UnhandledExceptionEventHandler(Object sender, UnhandledExceptionEventArgs e);
+}
diff --git a/src/mscorlib/shared/System/UnitySerializationHolder.cs b/src/mscorlib/shared/System/UnitySerializationHolder.cs
new file mode 100644
index 0000000000..f1957981d3
--- /dev/null
+++ b/src/mscorlib/shared/System/UnitySerializationHolder.cs
@@ -0,0 +1,329 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Runtime.Serialization;
+using System.Reflection;
+using System.Globalization;
+using System.Runtime.Versioning;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+
+namespace System
+{
+ [Serializable]
+ // Holds classes (Empty, Null, Missing) for which we guarantee that there is only ever one instance of.
+#if CORECLR
+ internal
+#else
+ public // On CoreRT, this must be public because of the Reflection.Core/CoreLib divide and the need to whitelist past the ReflectionBlock.
+#endif
+ class UnitySerializationHolder : ISerializable, IObjectReference
+ {
+#region Internal Constants
+ internal const int EmptyUnity = 0x0001;
+ internal const int NullUnity = 0x0002;
+ internal const int MissingUnity = 0x0003;
+ internal const int RuntimeTypeUnity = 0x0004;
+ public const int ModuleUnity = 0x0005;
+ public const int AssemblyUnity = 0x0006;
+ internal const int GenericParameterTypeUnity = 0x0007;
+ internal const int PartialInstantiationTypeUnity = 0x0008;
+
+ internal const int Pointer = 0x0001;
+ internal const int Array = 0x0002;
+ internal const int SzArray = 0x0003;
+ internal const int ByRef = 0x0004;
+#endregion
+
+#region Internal Static Members
+ internal static void GetUnitySerializationInfo(SerializationInfo info, Missing missing)
+ {
+ info.SetType(typeof(UnitySerializationHolder));
+ info.AddValue("UnityType", MissingUnity);
+ }
+
+ internal static Type AddElementTypes(SerializationInfo info, Type type)
+ {
+ List<int> elementTypes = new List<int>();
+ while (type.HasElementType)
+ {
+ if (type.IsSZArray)
+ {
+ elementTypes.Add(SzArray);
+ }
+ else if (type.IsArray)
+ {
+ elementTypes.Add(type.GetArrayRank());
+ elementTypes.Add(Array);
+ }
+ else if (type.IsPointer)
+ {
+ elementTypes.Add(Pointer);
+ }
+ else if (type.IsByRef)
+ {
+ elementTypes.Add(ByRef);
+ }
+
+ type = type.GetElementType();
+ }
+
+ info.AddValue("ElementTypes", elementTypes.ToArray(), typeof(int[]));
+
+ return type;
+ }
+
+ internal Type MakeElementTypes(Type type)
+ {
+ for (int i = _elementTypes.Length - 1; i >= 0; i--)
+ {
+ if (_elementTypes[i] == SzArray)
+ {
+ type = type.MakeArrayType();
+ }
+ else if (_elementTypes[i] == Array)
+ {
+ type = type.MakeArrayType(_elementTypes[--i]);
+ }
+ else if ((_elementTypes[i] == Pointer))
+ {
+ type = type.MakePointerType();
+ }
+ else if ((_elementTypes[i] == ByRef))
+ {
+ type = type.MakeByRefType();
+ }
+ }
+
+ return type;
+ }
+
+ public static void GetUnitySerializationInfo(SerializationInfo info, Type type)
+ {
+ Type rootElementType = type;
+ while (rootElementType.HasElementType)
+ {
+ rootElementType = rootElementType.GetElementType();
+ }
+
+ if (rootElementType.IsGenericParameter)
+ {
+ type = AddElementTypes(info, type);
+ info.SetType(typeof(UnitySerializationHolder));
+ info.AddValue("UnityType", GenericParameterTypeUnity);
+ info.AddValue("GenericParameterPosition", type.GenericParameterPosition);
+ info.AddValue("DeclaringMethod", type.DeclaringMethod, typeof(MethodBase));
+ info.AddValue("DeclaringType", type.DeclaringType, typeof(Type));
+
+ return;
+ }
+
+ int unityType = RuntimeTypeUnity;
+
+ if (!type.IsGenericTypeDefinition && type.ContainsGenericParameters)
+ {
+ // Partial instantiation
+ unityType = PartialInstantiationTypeUnity;
+ type = AddElementTypes(info, type);
+ info.AddValue("GenericArguments", type.GetGenericArguments(), typeof(Type[]));
+ type = type.GetGenericTypeDefinition();
+ }
+
+ GetUnitySerializationInfo(info, unityType, type.FullName, type.Assembly);
+ }
+
+ public static void GetUnitySerializationInfo(
+ SerializationInfo info, int unityType, string data, Assembly assembly)
+ {
+ // A helper method that returns the SerializationInfo that a class utilizing
+ // UnitySerializationHelper should return from a call to GetObjectData. It contains
+ // the unityType (defined above) and any optional data (used only for the reflection
+ // types.)
+
+ info.SetType(typeof(UnitySerializationHolder));
+ info.AddValue("Data", data, typeof(string));
+ info.AddValue("UnityType", unityType);
+
+ string assemName;
+
+ if (assembly == null)
+ {
+ assemName = string.Empty;
+ }
+ else
+ {
+ assemName = assembly.FullName;
+ }
+
+ info.AddValue("AssemblyName", assemName);
+ }
+#endregion
+
+#region Private Data Members
+ private readonly Type[] _instantiation;
+ private readonly int[] _elementTypes;
+ private readonly int _genericParameterPosition;
+ private readonly Type _declaringType;
+ private readonly MethodBase _declaringMethod;
+ private readonly string _data;
+ private readonly string _assemblyName;
+ private int _unityType;
+#endregion
+
+#region Constructor
+ public UnitySerializationHolder(SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException(nameof(info));
+ Contract.EndContractBlock();
+
+ _unityType = info.GetInt32("UnityType");
+
+ if (_unityType == MissingUnity)
+ return;
+
+ if (_unityType == GenericParameterTypeUnity)
+ {
+ _declaringMethod = info.GetValue("DeclaringMethod", typeof(MethodBase)) as MethodBase;
+ _declaringType = info.GetValue("DeclaringType", typeof(Type)) as Type;
+ _genericParameterPosition = info.GetInt32("GenericParameterPosition");
+ _elementTypes = info.GetValue("ElementTypes", typeof(int[])) as int[];
+
+ return;
+ }
+
+ if (_unityType == PartialInstantiationTypeUnity)
+ {
+ _instantiation = info.GetValue("GenericArguments", typeof(Type[])) as Type[];
+ _elementTypes = info.GetValue("ElementTypes", typeof(int[])) as int[];
+ }
+
+ _data = info.GetString("Data");
+ _assemblyName = info.GetString("AssemblyName");
+ }
+#endregion
+
+#region Private Methods
+ private void ThrowInsufficientInformation(string field)
+ {
+ throw new SerializationException(
+ SR.Format(SR.Serialization_InsufficientDeserializationState, field));
+ }
+#endregion
+
+#region ISerializable
+ public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ throw new NotSupportedException(SR.NotSupported_UnitySerHolder);
+ }
+#endregion
+
+#region IObjectReference
+ public virtual object GetRealObject(StreamingContext context)
+ {
+ // GetRealObject uses the data we have in _data and _unityType to do a lookup on the correct
+ // object to return. We have specific code here to handle the different types which we support.
+ // The reflection types (Assembly, Module, and Type) have to be looked up through their static
+ // accessors by name.
+
+ Assembly assembly;
+
+ switch (_unityType)
+ {
+ case EmptyUnity:
+ {
+ return Empty.Value;
+ }
+
+ case NullUnity:
+ {
+ return DBNull.Value;
+ }
+
+ case MissingUnity:
+ {
+ return Missing.Value;
+ }
+
+ case PartialInstantiationTypeUnity:
+ {
+ _unityType = RuntimeTypeUnity;
+ Type definition = GetRealObject(context) as Type;
+ _unityType = PartialInstantiationTypeUnity;
+
+ if (_instantiation[0] == null)
+ return null;
+
+ return MakeElementTypes(definition.MakeGenericType(_instantiation));
+ }
+
+ case GenericParameterTypeUnity:
+ {
+ if (_declaringMethod == null && _declaringType == null)
+ ThrowInsufficientInformation("DeclaringMember");
+
+ if (_declaringMethod != null)
+ return _declaringMethod.GetGenericArguments()[_genericParameterPosition];
+
+ return MakeElementTypes(_declaringType.GetGenericArguments()[_genericParameterPosition]);
+ }
+
+ case RuntimeTypeUnity:
+ {
+ if (_data == null || _data.Length == 0)
+ ThrowInsufficientInformation("Data");
+
+ if (_assemblyName == null)
+ ThrowInsufficientInformation("AssemblyName");
+
+ if (_assemblyName.Length == 0)
+ return Type.GetType(_data, true, false);
+
+ assembly = Assembly.Load(_assemblyName);
+
+ Type t = assembly.GetType(_data, true, false);
+
+ return t;
+ }
+
+ case ModuleUnity:
+ {
+ if (_data == null || _data.Length == 0)
+ ThrowInsufficientInformation("Data");
+
+ if (_assemblyName == null)
+ ThrowInsufficientInformation("AssemblyName");
+
+ assembly = Assembly.Load(_assemblyName);
+
+ Module namedModule = assembly.GetModule(_data);
+
+ if (namedModule == null)
+ throw new SerializationException(
+ SR.Format(SR.Serialization_UnableToFindModule, _data, _assemblyName));
+
+ return namedModule;
+ }
+
+ case AssemblyUnity:
+ {
+ if (_data == null || _data.Length == 0)
+ ThrowInsufficientInformation("Data");
+
+ if (_assemblyName == null)
+ ThrowInsufficientInformation("AssemblyName");
+
+ assembly = Assembly.Load(_assemblyName);
+
+ return assembly;
+ }
+
+ default:
+ throw new ArgumentException(SR.Argument_InvalidUnity);
+ }
+ }
+#endregion
+ }
+}
diff --git a/src/mscorlib/shared/System/ValueTuple.cs b/src/mscorlib/shared/System/ValueTuple.cs
new file mode 100644
index 0000000000..e0cd02e914
--- /dev/null
+++ b/src/mscorlib/shared/System/ValueTuple.cs
@@ -0,0 +1,2324 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using HashHelpers = System.Numerics.Hashing.HashHelpers;
+
+namespace System
+{
+ /// <summary>
+ /// Helper so we can call some tuple methods recursively without knowing the underlying types.
+ /// </summary>
+ internal interface IValueTupleInternal : ITuple
+ {
+ int GetHashCode(IEqualityComparer comparer);
+ string ToStringEnd();
+ }
+
+ /// <summary>
+ /// The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies tuples in C# and struct tuples in F#.
+ /// Aside from created via language syntax, they are most easily created via the ValueTuple.Create factory methods.
+ /// The System.ValueTuple types differ from the System.Tuple types in that:
+ /// - they are structs rather than classes,
+ /// - they are mutable rather than readonly, and
+ /// - their members (such as Item1, Item2, etc) are fields rather than properties.
+ /// </summary>
+ [Serializable]
+ public struct ValueTuple
+ : IEquatable<ValueTuple>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable<ValueTuple>, IValueTupleInternal, ITuple
+ {
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple"/> instance is equal to a specified object.
+ /// </summary>
+ /// <param name="obj">The object to compare with this instance.</param>
+ /// <returns><see langword="true"/> if <paramref name="obj"/> is a <see cref="ValueTuple"/>.</returns>
+ public override bool Equals(object obj)
+ {
+ return obj is ValueTuple;
+ }
+
+ /// <summary>Returns a value indicating whether this instance is equal to a specified value.</summary>
+ /// <param name="other">An instance to compare to this instance.</param>
+ /// <returns>true if <paramref name="other"/> has the same value as this instance; otherwise, false.</returns>
+ public bool Equals(ValueTuple other)
+ {
+ return true;
+ }
+
+ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
+ {
+ return other is ValueTuple;
+ }
+
+ int IComparable.CompareTo(object other)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ return 0;
+ }
+
+ /// <summary>Compares this instance to a specified instance and returns an indication of their relative values.</summary>
+ /// <param name="other">An instance to compare.</param>
+ /// <returns>
+ /// A signed number indicating the relative values of this instance and <paramref name="other"/>.
+ /// Returns less than zero if this instance is less than <paramref name="other"/>, zero if this
+ /// instance is equal to <paramref name="other"/>, and greater than zero if this instance is greater
+ /// than <paramref name="other"/>.
+ /// </returns>
+ public int CompareTo(ValueTuple other)
+ {
+ return 0;
+ }
+
+ int IStructuralComparable.CompareTo(object other, IComparer comparer)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ return 0;
+ }
+
+ /// <summary>Returns the hash code for this instance.</summary>
+ /// <returns>A 32-bit signed integer hash code.</returns>
+ public override int GetHashCode()
+ {
+ return 0;
+ }
+
+ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
+ {
+ return 0;
+ }
+
+ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
+ {
+ return 0;
+ }
+
+ /// <summary>
+ /// Returns a string that represents the value of this <see cref="ValueTuple"/> instance.
+ /// </summary>
+ /// <returns>The string representation of this <see cref="ValueTuple"/> instance.</returns>
+ /// <remarks>
+ /// The string returned by this method takes the form <c>()</c>.
+ /// </remarks>
+ public override string ToString()
+ {
+ return "()";
+ }
+
+ string IValueTupleInternal.ToStringEnd()
+ {
+ return ")";
+ }
+
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int ITuple.Length => 0;
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object ITuple.this[int index]
+ {
+ get
+ {
+ throw new IndexOutOfRangeException();
+ }
+ }
+
+ /// <summary>Creates a new struct 0-tuple.</summary>
+ /// <returns>A 0-tuple.</returns>
+ public static ValueTuple Create() =>
+ new ValueTuple();
+
+ /// <summary>Creates a new struct 1-tuple, or singleton.</summary>
+ /// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
+ /// <param name="item1">The value of the first component of the tuple.</param>
+ /// <returns>A 1-tuple (singleton) whose value is (item1).</returns>
+ public static ValueTuple<T1> Create<T1>(T1 item1) =>
+ new ValueTuple<T1>(item1);
+
+ /// <summary>Creates a new struct 2-tuple, or pair.</summary>
+ /// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
+ /// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
+ /// <param name="item1">The value of the first component of the tuple.</param>
+ /// <param name="item2">The value of the second component of the tuple.</param>
+ /// <returns>A 2-tuple (pair) whose value is (item1, item2).</returns>
+ public static ValueTuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2) =>
+ new ValueTuple<T1, T2>(item1, item2);
+
+ /// <summary>Creates a new struct 3-tuple, or triple.</summary>
+ /// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
+ /// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
+ /// <typeparam name="T3">The type of the third component of the tuple.</typeparam>
+ /// <param name="item1">The value of the first component of the tuple.</param>
+ /// <param name="item2">The value of the second component of the tuple.</param>
+ /// <param name="item3">The value of the third component of the tuple.</param>
+ /// <returns>A 3-tuple (triple) whose value is (item1, item2, item3).</returns>
+ public static ValueTuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3) =>
+ new ValueTuple<T1, T2, T3>(item1, item2, item3);
+
+ /// <summary>Creates a new struct 4-tuple, or quadruple.</summary>
+ /// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
+ /// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
+ /// <typeparam name="T3">The type of the third component of the tuple.</typeparam>
+ /// <typeparam name="T4">The type of the fourth component of the tuple.</typeparam>
+ /// <param name="item1">The value of the first component of the tuple.</param>
+ /// <param name="item2">The value of the second component of the tuple.</param>
+ /// <param name="item3">The value of the third component of the tuple.</param>
+ /// <param name="item4">The value of the fourth component of the tuple.</param>
+ /// <returns>A 4-tuple (quadruple) whose value is (item1, item2, item3, item4).</returns>
+ public static ValueTuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 item1, T2 item2, T3 item3, T4 item4) =>
+ new ValueTuple<T1, T2, T3, T4>(item1, item2, item3, item4);
+
+ /// <summary>Creates a new struct 5-tuple, or quintuple.</summary>
+ /// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
+ /// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
+ /// <typeparam name="T3">The type of the third component of the tuple.</typeparam>
+ /// <typeparam name="T4">The type of the fourth component of the tuple.</typeparam>
+ /// <typeparam name="T5">The type of the fifth component of the tuple.</typeparam>
+ /// <param name="item1">The value of the first component of the tuple.</param>
+ /// <param name="item2">The value of the second component of the tuple.</param>
+ /// <param name="item3">The value of the third component of the tuple.</param>
+ /// <param name="item4">The value of the fourth component of the tuple.</param>
+ /// <param name="item5">The value of the fifth component of the tuple.</param>
+ /// <returns>A 5-tuple (quintuple) whose value is (item1, item2, item3, item4, item5).</returns>
+ public static ValueTuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) =>
+ new ValueTuple<T1, T2, T3, T4, T5>(item1, item2, item3, item4, item5);
+
+ /// <summary>Creates a new struct 6-tuple, or sextuple.</summary>
+ /// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
+ /// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
+ /// <typeparam name="T3">The type of the third component of the tuple.</typeparam>
+ /// <typeparam name="T4">The type of the fourth component of the tuple.</typeparam>
+ /// <typeparam name="T5">The type of the fifth component of the tuple.</typeparam>
+ /// <typeparam name="T6">The type of the sixth component of the tuple.</typeparam>
+ /// <param name="item1">The value of the first component of the tuple.</param>
+ /// <param name="item2">The value of the second component of the tuple.</param>
+ /// <param name="item3">The value of the third component of the tuple.</param>
+ /// <param name="item4">The value of the fourth component of the tuple.</param>
+ /// <param name="item5">The value of the fifth component of the tuple.</param>
+ /// <param name="item6">The value of the sixth component of the tuple.</param>
+ /// <returns>A 6-tuple (sextuple) whose value is (item1, item2, item3, item4, item5, item6).</returns>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) =>
+ new ValueTuple<T1, T2, T3, T4, T5, T6>(item1, item2, item3, item4, item5, item6);
+
+ /// <summary>Creates a new struct 7-tuple, or septuple.</summary>
+ /// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
+ /// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
+ /// <typeparam name="T3">The type of the third component of the tuple.</typeparam>
+ /// <typeparam name="T4">The type of the fourth component of the tuple.</typeparam>
+ /// <typeparam name="T5">The type of the fifth component of the tuple.</typeparam>
+ /// <typeparam name="T6">The type of the sixth component of the tuple.</typeparam>
+ /// <typeparam name="T7">The type of the seventh component of the tuple.</typeparam>
+ /// <param name="item1">The value of the first component of the tuple.</param>
+ /// <param name="item2">The value of the second component of the tuple.</param>
+ /// <param name="item3">The value of the third component of the tuple.</param>
+ /// <param name="item4">The value of the fourth component of the tuple.</param>
+ /// <param name="item5">The value of the fifth component of the tuple.</param>
+ /// <param name="item6">The value of the sixth component of the tuple.</param>
+ /// <param name="item7">The value of the seventh component of the tuple.</param>
+ /// <returns>A 7-tuple (septuple) whose value is (item1, item2, item3, item4, item5, item6, item7).</returns>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) =>
+ new ValueTuple<T1, T2, T3, T4, T5, T6, T7>(item1, item2, item3, item4, item5, item6, item7);
+
+ /// <summary>Creates a new struct 8-tuple, or octuple.</summary>
+ /// <typeparam name="T1">The type of the first component of the tuple.</typeparam>
+ /// <typeparam name="T2">The type of the second component of the tuple.</typeparam>
+ /// <typeparam name="T3">The type of the third component of the tuple.</typeparam>
+ /// <typeparam name="T4">The type of the fourth component of the tuple.</typeparam>
+ /// <typeparam name="T5">The type of the fifth component of the tuple.</typeparam>
+ /// <typeparam name="T6">The type of the sixth component of the tuple.</typeparam>
+ /// <typeparam name="T7">The type of the seventh component of the tuple.</typeparam>
+ /// <typeparam name="T8">The type of the eighth component of the tuple.</typeparam>
+ /// <param name="item1">The value of the first component of the tuple.</param>
+ /// <param name="item2">The value of the second component of the tuple.</param>
+ /// <param name="item3">The value of the third component of the tuple.</param>
+ /// <param name="item4">The value of the fourth component of the tuple.</param>
+ /// <param name="item5">The value of the fifth component of the tuple.</param>
+ /// <param name="item6">The value of the sixth component of the tuple.</param>
+ /// <param name="item7">The value of the seventh component of the tuple.</param>
+ /// <param name="item8">The value of the eighth component of the tuple.</param>
+ /// <returns>An 8-tuple (octuple) whose value is (item1, item2, item3, item4, item5, item6, item7, item8).</returns>
+ public static ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) =>
+ new ValueTuple<T1, T2, T3, T4, T5, T6, T7, ValueTuple<T8>>(item1, item2, item3, item4, item5, item6, item7, ValueTuple.Create(item8));
+
+ internal static int CombineHashCodes(int h1, int h2)
+ {
+ return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2);
+ }
+
+ internal static int CombineHashCodes(int h1, int h2, int h3)
+ {
+ return HashHelpers.Combine(CombineHashCodes(h1, h2), h3);
+ }
+
+ internal static int CombineHashCodes(int h1, int h2, int h3, int h4)
+ {
+ return HashHelpers.Combine(CombineHashCodes(h1, h2, h3), h4);
+ }
+
+ internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5)
+ {
+ return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4), h5);
+ }
+
+ internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6)
+ {
+ return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5), h6);
+ }
+
+ internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7)
+ {
+ return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6), h7);
+ }
+
+ internal static int CombineHashCodes(int h1, int h2, int h3, int h4, int h5, int h6, int h7, int h8)
+ {
+ return HashHelpers.Combine(CombineHashCodes(h1, h2, h3, h4, h5, h6, h7), h8);
+ }
+ }
+
+ /// <summary>Represents a 1-tuple, or singleton, as a value type.</summary>
+ /// <typeparam name="T1">The type of the tuple's only component.</typeparam>
+ [Serializable]
+ public struct ValueTuple<T1>
+ : IEquatable<ValueTuple<T1>>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable<ValueTuple<T1>>, IValueTupleInternal, ITuple
+ {
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1}"/> instance's first component.
+ /// </summary>
+ public T1 Item1;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueTuple{T1}"/> value type.
+ /// </summary>
+ /// <param name="item1">The value of the tuple's first component.</param>
+ public ValueTuple(T1 item1)
+ {
+ Item1 = item1;
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1}"/> instance is equal to a specified object.
+ /// </summary>
+ /// <param name="obj">The object to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified object; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="obj"/> parameter is considered to be equal to the current instance under the following conditions:
+ /// <list type="bullet">
+ /// <item><description>It is a <see cref="ValueTuple{T1}"/> value type.</description></item>
+ /// <item><description>Its components are of the same types as those of the current instance.</description></item>
+ /// <item><description>Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component.</description></item>
+ /// </list>
+ /// </remarks>
+ public override bool Equals(object obj)
+ {
+ return obj is ValueTuple<T1> && Equals((ValueTuple<T1>)obj);
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1}"/>
+ /// instance is equal to a specified <see cref="ValueTuple{T1}"/>.
+ /// </summary>
+ /// <param name="other">The tuple to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified tuple; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="other"/> parameter is considered to be equal to the current instance if each of its field
+ /// is equal to that of the current instance, using the default comparer for that field's type.
+ /// </remarks>
+ public bool Equals(ValueTuple<T1> other)
+ {
+ return EqualityComparer<T1>.Default.Equals(Item1, other.Item1);
+ }
+
+ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
+ {
+ if (other == null || !(other is ValueTuple<T1>)) return false;
+
+ var objTuple = (ValueTuple<T1>)other;
+
+ return comparer.Equals(Item1, objTuple.Item1);
+ }
+
+ int IComparable.CompareTo(object other)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ var objTuple = (ValueTuple<T1>)other;
+
+ return Comparer<T1>.Default.Compare(Item1, objTuple.Item1);
+ }
+
+ /// <summary>Compares this instance to a specified instance and returns an indication of their relative values.</summary>
+ /// <param name="other">An instance to compare.</param>
+ /// <returns>
+ /// A signed number indicating the relative values of this instance and <paramref name="other"/>.
+ /// Returns less than zero if this instance is less than <paramref name="other"/>, zero if this
+ /// instance is equal to <paramref name="other"/>, and greater than zero if this instance is greater
+ /// than <paramref name="other"/>.
+ /// </returns>
+ public int CompareTo(ValueTuple<T1> other)
+ {
+ return Comparer<T1>.Default.Compare(Item1, other.Item1);
+ }
+
+ int IStructuralComparable.CompareTo(object other, IComparer comparer)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ var objTuple = (ValueTuple<T1>)other;
+
+ return comparer.Compare(Item1, objTuple.Item1);
+ }
+
+ /// <summary>
+ /// Returns the hash code for the current <see cref="ValueTuple{T1}"/> instance.
+ /// </summary>
+ /// <returns>A 32-bit signed integer hash code.</returns>
+ public override int GetHashCode()
+ {
+ return Item1?.GetHashCode() ?? 0;
+ }
+
+ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
+ {
+ return comparer.GetHashCode(Item1);
+ }
+
+ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
+ {
+ return comparer.GetHashCode(Item1);
+ }
+
+ /// <summary>
+ /// Returns a string that represents the value of this <see cref="ValueTuple{T1}"/> instance.
+ /// </summary>
+ /// <returns>The string representation of this <see cref="ValueTuple{T1}"/> instance.</returns>
+ /// <remarks>
+ /// The string returned by this method takes the form <c>(Item1)</c>,
+ /// where <c>Item1</c> represents the value of <see cref="Item1"/>. If the field is <see langword="null"/>,
+ /// it is represented as <see cref="string.Empty"/>.
+ /// </remarks>
+ public override string ToString()
+ {
+ return "(" + Item1?.ToString() + ")";
+ }
+
+ string IValueTupleInternal.ToStringEnd()
+ {
+ return Item1?.ToString() + ")";
+ }
+
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int ITuple.Length => 1;
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object ITuple.this[int index]
+ {
+ get
+ {
+ if (index != 0)
+ {
+ throw new IndexOutOfRangeException();
+ }
+ return Item1;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Represents a 2-tuple, or pair, as a value type.
+ /// </summary>
+ /// <typeparam name="T1">The type of the tuple's first component.</typeparam>
+ /// <typeparam name="T2">The type of the tuple's second component.</typeparam>
+ [Serializable]
+ [StructLayout(LayoutKind.Auto)]
+ public struct ValueTuple<T1, T2>
+ : IEquatable<ValueTuple<T1, T2>>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable<ValueTuple<T1, T2>>, IValueTupleInternal, ITuple
+ {
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2}"/> instance's first component.
+ /// </summary>
+ public T1 Item1;
+
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2}"/> instance's first component.
+ /// </summary>
+ public T2 Item2;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueTuple{T1, T2}"/> value type.
+ /// </summary>
+ /// <param name="item1">The value of the tuple's first component.</param>
+ /// <param name="item2">The value of the tuple's second component.</param>
+ public ValueTuple(T1 item1, T2 item2)
+ {
+ Item1 = item1;
+ Item2 = item2;
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2}"/> instance is equal to a specified object.
+ /// </summary>
+ /// <param name="obj">The object to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified object; otherwise, <see langword="false"/>.</returns>
+ ///
+ /// <remarks>
+ /// The <paramref name="obj"/> parameter is considered to be equal to the current instance under the following conditions:
+ /// <list type="bullet">
+ /// <item><description>It is a <see cref="ValueTuple{T1, T2}"/> value type.</description></item>
+ /// <item><description>Its components are of the same types as those of the current instance.</description></item>
+ /// <item><description>Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component.</description></item>
+ /// </list>
+ /// </remarks>
+ public override bool Equals(object obj)
+ {
+ return obj is ValueTuple<T1, T2> && Equals((ValueTuple<T1, T2>)obj);
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2}"/> instance is equal to a specified <see cref="ValueTuple{T1, T2}"/>.
+ /// </summary>
+ /// <param name="other">The tuple to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified tuple; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="other"/> parameter is considered to be equal to the current instance if each of its fields
+ /// are equal to that of the current instance, using the default comparer for that field's type.
+ /// </remarks>
+ public bool Equals(ValueTuple<T1, T2> other)
+ {
+ return EqualityComparer<T1>.Default.Equals(Item1, other.Item1)
+ && EqualityComparer<T2>.Default.Equals(Item2, other.Item2);
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2}"/> instance is equal to a specified object based on a specified comparison method.
+ /// </summary>
+ /// <param name="other">The object to compare with this instance.</param>
+ /// <param name="comparer">An object that defines the method to use to evaluate whether the two objects are equal.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified object; otherwise, <see langword="false"/>.</returns>
+ ///
+ /// <remarks>
+ /// This member is an explicit interface member implementation. It can be used only when the
+ /// <see cref="ValueTuple{T1, T2}"/> instance is cast to an <see cref="IStructuralEquatable"/> interface.
+ ///
+ /// The <see cref="IEqualityComparer.Equals"/> implementation is called only if <c>other</c> is not <see langword="null"/>,
+ /// and if it can be successfully cast (in C#) or converted (in Visual Basic) to a <see cref="ValueTuple{T1, T2}"/>
+ /// whose components are of the same types as those of the current instance. The IStructuralEquatable.Equals(Object, IEqualityComparer) method
+ /// first passes the <see cref="Item1"/> values of the <see cref="ValueTuple{T1, T2}"/> objects to be compared to the
+ /// <see cref="IEqualityComparer.Equals"/> implementation. If this method call returns <see langword="true"/>, the method is
+ /// called again and passed the <see cref="Item2"/> values of the two <see cref="ValueTuple{T1, T2}"/> instances.
+ /// </remarks>
+ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
+ {
+ if (other == null || !(other is ValueTuple<T1, T2>)) return false;
+
+ var objTuple = (ValueTuple<T1, T2>)other;
+
+ return comparer.Equals(Item1, objTuple.Item1)
+ && comparer.Equals(Item2, objTuple.Item2);
+ }
+
+ int IComparable.CompareTo(object other)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ return CompareTo((ValueTuple<T1, T2>)other);
+ }
+
+ /// <summary>Compares this instance to a specified instance and returns an indication of their relative values.</summary>
+ /// <param name="other">An instance to compare.</param>
+ /// <returns>
+ /// A signed number indicating the relative values of this instance and <paramref name="other"/>.
+ /// Returns less than zero if this instance is less than <paramref name="other"/>, zero if this
+ /// instance is equal to <paramref name="other"/>, and greater than zero if this instance is greater
+ /// than <paramref name="other"/>.
+ /// </returns>
+ public int CompareTo(ValueTuple<T1, T2> other)
+ {
+ int c = Comparer<T1>.Default.Compare(Item1, other.Item1);
+ if (c != 0) return c;
+
+ return Comparer<T2>.Default.Compare(Item2, other.Item2);
+ }
+
+ int IStructuralComparable.CompareTo(object other, IComparer comparer)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ var objTuple = (ValueTuple<T1, T2>)other;
+
+ int c = comparer.Compare(Item1, objTuple.Item1);
+ if (c != 0) return c;
+
+ return comparer.Compare(Item2, objTuple.Item2);
+ }
+
+ /// <summary>
+ /// Returns the hash code for the current <see cref="ValueTuple{T1, T2}"/> instance.
+ /// </summary>
+ /// <returns>A 32-bit signed integer hash code.</returns>
+ public override int GetHashCode()
+ {
+ return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
+ Item2?.GetHashCode() ?? 0);
+ }
+
+ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ private int GetHashCodeCore(IEqualityComparer comparer)
+ {
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1),
+ comparer.GetHashCode(Item2));
+ }
+
+ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ /// <summary>
+ /// Returns a string that represents the value of this <see cref="ValueTuple{T1, T2}"/> instance.
+ /// </summary>
+ /// <returns>The string representation of this <see cref="ValueTuple{T1, T2}"/> instance.</returns>
+ /// <remarks>
+ /// The string returned by this method takes the form <c>(Item1, Item2)</c>,
+ /// where <c>Item1</c> and <c>Item2</c> represent the values of the <see cref="Item1"/>
+ /// and <see cref="Item2"/> fields. If either field value is <see langword="null"/>,
+ /// it is represented as <see cref="String.Empty"/>.
+ /// </remarks>
+ public override string ToString()
+ {
+ return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ")";
+ }
+
+ string IValueTupleInternal.ToStringEnd()
+ {
+ return Item1?.ToString() + ", " + Item2?.ToString() + ")";
+ }
+
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int ITuple.Length => 2;
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object ITuple.this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return Item1;
+ case 1:
+ return Item2;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Represents a 3-tuple, or triple, as a value type.
+ /// </summary>
+ /// <typeparam name="T1">The type of the tuple's first component.</typeparam>
+ /// <typeparam name="T2">The type of the tuple's second component.</typeparam>
+ /// <typeparam name="T3">The type of the tuple's third component.</typeparam>
+ [Serializable]
+ [StructLayout(LayoutKind.Auto)]
+ public struct ValueTuple<T1, T2, T3>
+ : IEquatable<ValueTuple<T1, T2, T3>>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable<ValueTuple<T1, T2, T3>>, IValueTupleInternal, ITuple
+ {
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3}"/> instance's first component.
+ /// </summary>
+ public T1 Item1;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3}"/> instance's second component.
+ /// </summary>
+ public T2 Item2;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3}"/> instance's third component.
+ /// </summary>
+ public T3 Item3;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueTuple{T1, T2, T3}"/> value type.
+ /// </summary>
+ /// <param name="item1">The value of the tuple's first component.</param>
+ /// <param name="item2">The value of the tuple's second component.</param>
+ /// <param name="item3">The value of the tuple's third component.</param>
+ public ValueTuple(T1 item1, T2 item2, T3 item3)
+ {
+ Item1 = item1;
+ Item2 = item2;
+ Item3 = item3;
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3}"/> instance is equal to a specified object.
+ /// </summary>
+ /// <param name="obj">The object to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified object; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="obj"/> parameter is considered to be equal to the current instance under the following conditions:
+ /// <list type="bullet">
+ /// <item><description>It is a <see cref="ValueTuple{T1, T2, T3}"/> value type.</description></item>
+ /// <item><description>Its components are of the same types as those of the current instance.</description></item>
+ /// <item><description>Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component.</description></item>
+ /// </list>
+ /// </remarks>
+ public override bool Equals(object obj)
+ {
+ return obj is ValueTuple<T1, T2, T3> && Equals((ValueTuple<T1, T2, T3>)obj);
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3}"/>
+ /// instance is equal to a specified <see cref="ValueTuple{T1, T2, T3}"/>.
+ /// </summary>
+ /// <param name="other">The tuple to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified tuple; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="other"/> parameter is considered to be equal to the current instance if each of its fields
+ /// are equal to that of the current instance, using the default comparer for that field's type.
+ /// </remarks>
+ public bool Equals(ValueTuple<T1, T2, T3> other)
+ {
+ return EqualityComparer<T1>.Default.Equals(Item1, other.Item1)
+ && EqualityComparer<T2>.Default.Equals(Item2, other.Item2)
+ && EqualityComparer<T3>.Default.Equals(Item3, other.Item3);
+ }
+
+ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
+ {
+ if (other == null || !(other is ValueTuple<T1, T2, T3>)) return false;
+
+ var objTuple = (ValueTuple<T1, T2, T3>)other;
+
+ return comparer.Equals(Item1, objTuple.Item1)
+ && comparer.Equals(Item2, objTuple.Item2)
+ && comparer.Equals(Item3, objTuple.Item3);
+ }
+
+ int IComparable.CompareTo(object other)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ return CompareTo((ValueTuple<T1, T2, T3>)other);
+ }
+
+ /// <summary>Compares this instance to a specified instance and returns an indication of their relative values.</summary>
+ /// <param name="other">An instance to compare.</param>
+ /// <returns>
+ /// A signed number indicating the relative values of this instance and <paramref name="other"/>.
+ /// Returns less than zero if this instance is less than <paramref name="other"/>, zero if this
+ /// instance is equal to <paramref name="other"/>, and greater than zero if this instance is greater
+ /// than <paramref name="other"/>.
+ /// </returns>
+ public int CompareTo(ValueTuple<T1, T2, T3> other)
+ {
+ int c = Comparer<T1>.Default.Compare(Item1, other.Item1);
+ if (c != 0) return c;
+
+ c = Comparer<T2>.Default.Compare(Item2, other.Item2);
+ if (c != 0) return c;
+
+ return Comparer<T3>.Default.Compare(Item3, other.Item3);
+ }
+
+ int IStructuralComparable.CompareTo(object other, IComparer comparer)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ var objTuple = (ValueTuple<T1, T2, T3>)other;
+
+ int c = comparer.Compare(Item1, objTuple.Item1);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item2, objTuple.Item2);
+ if (c != 0) return c;
+
+ return comparer.Compare(Item3, objTuple.Item3);
+ }
+
+ /// <summary>
+ /// Returns the hash code for the current <see cref="ValueTuple{T1, T2, T3}"/> instance.
+ /// </summary>
+ /// <returns>A 32-bit signed integer hash code.</returns>
+ public override int GetHashCode()
+ {
+ return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
+ Item2?.GetHashCode() ?? 0,
+ Item3?.GetHashCode() ?? 0);
+ }
+
+ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ private int GetHashCodeCore(IEqualityComparer comparer)
+ {
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1),
+ comparer.GetHashCode(Item2),
+ comparer.GetHashCode(Item3));
+ }
+
+ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ /// <summary>
+ /// Returns a string that represents the value of this <see cref="ValueTuple{T1, T2, T3}"/> instance.
+ /// </summary>
+ /// <returns>The string representation of this <see cref="ValueTuple{T1, T2, T3}"/> instance.</returns>
+ /// <remarks>
+ /// The string returned by this method takes the form <c>(Item1, Item2, Item3)</c>.
+ /// If any field value is <see langword="null"/>, it is represented as <see cref="String.Empty"/>.
+ /// </remarks>
+ public override string ToString()
+ {
+ return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")";
+ }
+
+ string IValueTupleInternal.ToStringEnd()
+ {
+ return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ")";
+ }
+
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int ITuple.Length => 3;
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object ITuple.this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return Item1;
+ case 1:
+ return Item2;
+ case 2:
+ return Item3;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Represents a 4-tuple, or quadruple, as a value type.
+ /// </summary>
+ /// <typeparam name="T1">The type of the tuple's first component.</typeparam>
+ /// <typeparam name="T2">The type of the tuple's second component.</typeparam>
+ /// <typeparam name="T3">The type of the tuple's third component.</typeparam>
+ /// <typeparam name="T4">The type of the tuple's fourth component.</typeparam>
+ [Serializable]
+ [StructLayout(LayoutKind.Auto)]
+ public struct ValueTuple<T1, T2, T3, T4>
+ : IEquatable<ValueTuple<T1, T2, T3, T4>>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable<ValueTuple<T1, T2, T3, T4>>, IValueTupleInternal, ITuple
+ {
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4}"/> instance's first component.
+ /// </summary>
+ public T1 Item1;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4}"/> instance's second component.
+ /// </summary>
+ public T2 Item2;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4}"/> instance's third component.
+ /// </summary>
+ public T3 Item3;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4}"/> instance's fourth component.
+ /// </summary>
+ public T4 Item4;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueTuple{T1, T2, T3, T4}"/> value type.
+ /// </summary>
+ /// <param name="item1">The value of the tuple's first component.</param>
+ /// <param name="item2">The value of the tuple's second component.</param>
+ /// <param name="item3">The value of the tuple's third component.</param>
+ /// <param name="item4">The value of the tuple's fourth component.</param>
+ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4)
+ {
+ Item1 = item1;
+ Item2 = item2;
+ Item3 = item3;
+ Item4 = item4;
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4}"/> instance is equal to a specified object.
+ /// </summary>
+ /// <param name="obj">The object to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified object; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="obj"/> parameter is considered to be equal to the current instance under the following conditions:
+ /// <list type="bullet">
+ /// <item><description>It is a <see cref="ValueTuple{T1, T2, T3, T4}"/> value type.</description></item>
+ /// <item><description>Its components are of the same types as those of the current instance.</description></item>
+ /// <item><description>Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component.</description></item>
+ /// </list>
+ /// </remarks>
+ public override bool Equals(object obj)
+ {
+ return obj is ValueTuple<T1, T2, T3, T4> && Equals((ValueTuple<T1, T2, T3, T4>)obj);
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4}"/>
+ /// instance is equal to a specified <see cref="ValueTuple{T1, T2, T3, T4}"/>.
+ /// </summary>
+ /// <param name="other">The tuple to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified tuple; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="other"/> parameter is considered to be equal to the current instance if each of its fields
+ /// are equal to that of the current instance, using the default comparer for that field's type.
+ /// </remarks>
+ public bool Equals(ValueTuple<T1, T2, T3, T4> other)
+ {
+ return EqualityComparer<T1>.Default.Equals(Item1, other.Item1)
+ && EqualityComparer<T2>.Default.Equals(Item2, other.Item2)
+ && EqualityComparer<T3>.Default.Equals(Item3, other.Item3)
+ && EqualityComparer<T4>.Default.Equals(Item4, other.Item4);
+ }
+
+ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
+ {
+ if (other == null || !(other is ValueTuple<T1, T2, T3, T4>)) return false;
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4>)other;
+
+ return comparer.Equals(Item1, objTuple.Item1)
+ && comparer.Equals(Item2, objTuple.Item2)
+ && comparer.Equals(Item3, objTuple.Item3)
+ && comparer.Equals(Item4, objTuple.Item4);
+ }
+
+ int IComparable.CompareTo(object other)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ return CompareTo((ValueTuple<T1, T2, T3, T4>)other);
+ }
+
+ /// <summary>Compares this instance to a specified instance and returns an indication of their relative values.</summary>
+ /// <param name="other">An instance to compare.</param>
+ /// <returns>
+ /// A signed number indicating the relative values of this instance and <paramref name="other"/>.
+ /// Returns less than zero if this instance is less than <paramref name="other"/>, zero if this
+ /// instance is equal to <paramref name="other"/>, and greater than zero if this instance is greater
+ /// than <paramref name="other"/>.
+ /// </returns>
+ public int CompareTo(ValueTuple<T1, T2, T3, T4> other)
+ {
+ int c = Comparer<T1>.Default.Compare(Item1, other.Item1);
+ if (c != 0) return c;
+
+ c = Comparer<T2>.Default.Compare(Item2, other.Item2);
+ if (c != 0) return c;
+
+ c = Comparer<T3>.Default.Compare(Item3, other.Item3);
+ if (c != 0) return c;
+
+ return Comparer<T4>.Default.Compare(Item4, other.Item4);
+ }
+
+ int IStructuralComparable.CompareTo(object other, IComparer comparer)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4>)other;
+
+ int c = comparer.Compare(Item1, objTuple.Item1);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item2, objTuple.Item2);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item3, objTuple.Item3);
+ if (c != 0) return c;
+
+ return comparer.Compare(Item4, objTuple.Item4);
+ }
+
+ /// <summary>
+ /// Returns the hash code for the current <see cref="ValueTuple{T1, T2, T3, T4}"/> instance.
+ /// </summary>
+ /// <returns>A 32-bit signed integer hash code.</returns>
+ public override int GetHashCode()
+ {
+ return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
+ Item2?.GetHashCode() ?? 0,
+ Item3?.GetHashCode() ?? 0,
+ Item4?.GetHashCode() ?? 0);
+ }
+
+ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ private int GetHashCodeCore(IEqualityComparer comparer)
+ {
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1),
+ comparer.GetHashCode(Item2),
+ comparer.GetHashCode(Item3),
+ comparer.GetHashCode(Item4));
+ }
+
+ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ /// <summary>
+ /// Returns a string that represents the value of this <see cref="ValueTuple{T1, T2, T3, T4}"/> instance.
+ /// </summary>
+ /// <returns>The string representation of this <see cref="ValueTuple{T1, T2, T3, T4}"/> instance.</returns>
+ /// <remarks>
+ /// The string returned by this method takes the form <c>(Item1, Item2, Item3, Item4)</c>.
+ /// If any field value is <see langword="null"/>, it is represented as <see cref="String.Empty"/>.
+ /// </remarks>
+ public override string ToString()
+ {
+ return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")";
+ }
+
+ string IValueTupleInternal.ToStringEnd()
+ {
+ return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ")";
+ }
+
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int ITuple.Length => 4;
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object ITuple.this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return Item1;
+ case 1:
+ return Item2;
+ case 2:
+ return Item3;
+ case 3:
+ return Item4;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Represents a 5-tuple, or quintuple, as a value type.
+ /// </summary>
+ /// <typeparam name="T1">The type of the tuple's first component.</typeparam>
+ /// <typeparam name="T2">The type of the tuple's second component.</typeparam>
+ /// <typeparam name="T3">The type of the tuple's third component.</typeparam>
+ /// <typeparam name="T4">The type of the tuple's fourth component.</typeparam>
+ /// <typeparam name="T5">The type of the tuple's fifth component.</typeparam>
+ [Serializable]
+ [StructLayout(LayoutKind.Auto)]
+ public struct ValueTuple<T1, T2, T3, T4, T5>
+ : IEquatable<ValueTuple<T1, T2, T3, T4, T5>>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable<ValueTuple<T1, T2, T3, T4, T5>>, IValueTupleInternal, ITuple
+ {
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> instance's first component.
+ /// </summary>
+ public T1 Item1;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> instance's second component.
+ /// </summary>
+ public T2 Item2;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> instance's third component.
+ /// </summary>
+ public T3 Item3;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> instance's fourth component.
+ /// </summary>
+ public T4 Item4;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> instance's fifth component.
+ /// </summary>
+ public T5 Item5;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> value type.
+ /// </summary>
+ /// <param name="item1">The value of the tuple's first component.</param>
+ /// <param name="item2">The value of the tuple's second component.</param>
+ /// <param name="item3">The value of the tuple's third component.</param>
+ /// <param name="item4">The value of the tuple's fourth component.</param>
+ /// <param name="item5">The value of the tuple's fifth component.</param>
+ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
+ {
+ Item1 = item1;
+ Item2 = item2;
+ Item3 = item3;
+ Item4 = item4;
+ Item5 = item5;
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> instance is equal to a specified object.
+ /// </summary>
+ /// <param name="obj">The object to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified object; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="obj"/> parameter is considered to be equal to the current instance under the following conditions:
+ /// <list type="bullet">
+ /// <item><description>It is a <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> value type.</description></item>
+ /// <item><description>Its components are of the same types as those of the current instance.</description></item>
+ /// <item><description>Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component.</description></item>
+ /// </list>
+ /// </remarks>
+ public override bool Equals(object obj)
+ {
+ return obj is ValueTuple<T1, T2, T3, T4, T5> && Equals((ValueTuple<T1, T2, T3, T4, T5>)obj);
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4, T5}"/>
+ /// instance is equal to a specified <see cref="ValueTuple{T1, T2, T3, T4, T5}"/>.
+ /// </summary>
+ /// <param name="other">The tuple to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified tuple; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="other"/> parameter is considered to be equal to the current instance if each of its fields
+ /// are equal to that of the current instance, using the default comparer for that field's type.
+ /// </remarks>
+ public bool Equals(ValueTuple<T1, T2, T3, T4, T5> other)
+ {
+ return EqualityComparer<T1>.Default.Equals(Item1, other.Item1)
+ && EqualityComparer<T2>.Default.Equals(Item2, other.Item2)
+ && EqualityComparer<T3>.Default.Equals(Item3, other.Item3)
+ && EqualityComparer<T4>.Default.Equals(Item4, other.Item4)
+ && EqualityComparer<T5>.Default.Equals(Item5, other.Item5);
+ }
+
+ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
+ {
+ if (other == null || !(other is ValueTuple<T1, T2, T3, T4, T5>)) return false;
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4, T5>)other;
+
+ return comparer.Equals(Item1, objTuple.Item1)
+ && comparer.Equals(Item2, objTuple.Item2)
+ && comparer.Equals(Item3, objTuple.Item3)
+ && comparer.Equals(Item4, objTuple.Item4)
+ && comparer.Equals(Item5, objTuple.Item5);
+ }
+
+ int IComparable.CompareTo(object other)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4, T5>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ return CompareTo((ValueTuple<T1, T2, T3, T4, T5>)other);
+ }
+
+ /// <summary>Compares this instance to a specified instance and returns an indication of their relative values.</summary>
+ /// <param name="other">An instance to compare.</param>
+ /// <returns>
+ /// A signed number indicating the relative values of this instance and <paramref name="other"/>.
+ /// Returns less than zero if this instance is less than <paramref name="other"/>, zero if this
+ /// instance is equal to <paramref name="other"/>, and greater than zero if this instance is greater
+ /// than <paramref name="other"/>.
+ /// </returns>
+ public int CompareTo(ValueTuple<T1, T2, T3, T4, T5> other)
+ {
+ int c = Comparer<T1>.Default.Compare(Item1, other.Item1);
+ if (c != 0) return c;
+
+ c = Comparer<T2>.Default.Compare(Item2, other.Item2);
+ if (c != 0) return c;
+
+ c = Comparer<T3>.Default.Compare(Item3, other.Item3);
+ if (c != 0) return c;
+
+ c = Comparer<T4>.Default.Compare(Item4, other.Item4);
+ if (c != 0) return c;
+
+ return Comparer<T5>.Default.Compare(Item5, other.Item5);
+ }
+
+ int IStructuralComparable.CompareTo(object other, IComparer comparer)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4, T5>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4, T5>)other;
+
+ int c = comparer.Compare(Item1, objTuple.Item1);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item2, objTuple.Item2);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item3, objTuple.Item3);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item4, objTuple.Item4);
+ if (c != 0) return c;
+
+ return comparer.Compare(Item5, objTuple.Item5);
+ }
+
+ /// <summary>
+ /// Returns the hash code for the current <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> instance.
+ /// </summary>
+ /// <returns>A 32-bit signed integer hash code.</returns>
+ public override int GetHashCode()
+ {
+ return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
+ Item2?.GetHashCode() ?? 0,
+ Item3?.GetHashCode() ?? 0,
+ Item4?.GetHashCode() ?? 0,
+ Item5?.GetHashCode() ?? 0);
+ }
+
+ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ private int GetHashCodeCore(IEqualityComparer comparer)
+ {
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1),
+ comparer.GetHashCode(Item2),
+ comparer.GetHashCode(Item3),
+ comparer.GetHashCode(Item4),
+ comparer.GetHashCode(Item5));
+ }
+
+ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ /// <summary>
+ /// Returns a string that represents the value of this <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> instance.
+ /// </summary>
+ /// <returns>The string representation of this <see cref="ValueTuple{T1, T2, T3, T4, T5}"/> instance.</returns>
+ /// <remarks>
+ /// The string returned by this method takes the form <c>(Item1, Item2, Item3, Item4, Item5)</c>.
+ /// If any field value is <see langword="null"/>, it is represented as <see cref="String.Empty"/>.
+ /// </remarks>
+ public override string ToString()
+ {
+ return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")";
+ }
+
+ string IValueTupleInternal.ToStringEnd()
+ {
+ return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ")";
+ }
+
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int ITuple.Length => 5;
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object ITuple.this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return Item1;
+ case 1:
+ return Item2;
+ case 2:
+ return Item3;
+ case 3:
+ return Item4;
+ case 4:
+ return Item5;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Represents a 6-tuple, or sixtuple, as a value type.
+ /// </summary>
+ /// <typeparam name="T1">The type of the tuple's first component.</typeparam>
+ /// <typeparam name="T2">The type of the tuple's second component.</typeparam>
+ /// <typeparam name="T3">The type of the tuple's third component.</typeparam>
+ /// <typeparam name="T4">The type of the tuple's fourth component.</typeparam>
+ /// <typeparam name="T5">The type of the tuple's fifth component.</typeparam>
+ /// <typeparam name="T6">The type of the tuple's sixth component.</typeparam>
+ [Serializable]
+ [StructLayout(LayoutKind.Auto)]
+ public struct ValueTuple<T1, T2, T3, T4, T5, T6>
+ : IEquatable<ValueTuple<T1, T2, T3, T4, T5, T6>>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable<ValueTuple<T1, T2, T3, T4, T5, T6>>, IValueTupleInternal, ITuple
+ {
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance's first component.
+ /// </summary>
+ public T1 Item1;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance's second component.
+ /// </summary>
+ public T2 Item2;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance's third component.
+ /// </summary>
+ public T3 Item3;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance's fourth component.
+ /// </summary>
+ public T4 Item4;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance's fifth component.
+ /// </summary>
+ public T5 Item5;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance's sixth component.
+ /// </summary>
+ public T6 Item6;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> value type.
+ /// </summary>
+ /// <param name="item1">The value of the tuple's first component.</param>
+ /// <param name="item2">The value of the tuple's second component.</param>
+ /// <param name="item3">The value of the tuple's third component.</param>
+ /// <param name="item4">The value of the tuple's fourth component.</param>
+ /// <param name="item5">The value of the tuple's fifth component.</param>
+ /// <param name="item6">The value of the tuple's sixth component.</param>
+ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
+ {
+ Item1 = item1;
+ Item2 = item2;
+ Item3 = item3;
+ Item4 = item4;
+ Item5 = item5;
+ Item6 = item6;
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance is equal to a specified object.
+ /// </summary>
+ /// <param name="obj">The object to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified object; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="obj"/> parameter is considered to be equal to the current instance under the following conditions:
+ /// <list type="bullet">
+ /// <item><description>It is a <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> value type.</description></item>
+ /// <item><description>Its components are of the same types as those of the current instance.</description></item>
+ /// <item><description>Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component.</description></item>
+ /// </list>
+ /// </remarks>
+ public override bool Equals(object obj)
+ {
+ return obj is ValueTuple<T1, T2, T3, T4, T5, T6> && Equals((ValueTuple<T1, T2, T3, T4, T5, T6>)obj);
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/>
+ /// instance is equal to a specified <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/>.
+ /// </summary>
+ /// <param name="other">The tuple to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified tuple; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="other"/> parameter is considered to be equal to the current instance if each of its fields
+ /// are equal to that of the current instance, using the default comparer for that field's type.
+ /// </remarks>
+ public bool Equals(ValueTuple<T1, T2, T3, T4, T5, T6> other)
+ {
+ return EqualityComparer<T1>.Default.Equals(Item1, other.Item1)
+ && EqualityComparer<T2>.Default.Equals(Item2, other.Item2)
+ && EqualityComparer<T3>.Default.Equals(Item3, other.Item3)
+ && EqualityComparer<T4>.Default.Equals(Item4, other.Item4)
+ && EqualityComparer<T5>.Default.Equals(Item5, other.Item5)
+ && EqualityComparer<T6>.Default.Equals(Item6, other.Item6);
+ }
+
+ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
+ {
+ if (other == null || !(other is ValueTuple<T1, T2, T3, T4, T5, T6>)) return false;
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4, T5, T6>)other;
+
+ return comparer.Equals(Item1, objTuple.Item1)
+ && comparer.Equals(Item2, objTuple.Item2)
+ && comparer.Equals(Item3, objTuple.Item3)
+ && comparer.Equals(Item4, objTuple.Item4)
+ && comparer.Equals(Item5, objTuple.Item5)
+ && comparer.Equals(Item6, objTuple.Item6);
+ }
+
+ int IComparable.CompareTo(object other)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4, T5, T6>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ return CompareTo((ValueTuple<T1, T2, T3, T4, T5, T6>)other);
+ }
+
+ /// <summary>Compares this instance to a specified instance and returns an indication of their relative values.</summary>
+ /// <param name="other">An instance to compare.</param>
+ /// <returns>
+ /// A signed number indicating the relative values of this instance and <paramref name="other"/>.
+ /// Returns less than zero if this instance is less than <paramref name="other"/>, zero if this
+ /// instance is equal to <paramref name="other"/>, and greater than zero if this instance is greater
+ /// than <paramref name="other"/>.
+ /// </returns>
+ public int CompareTo(ValueTuple<T1, T2, T3, T4, T5, T6> other)
+ {
+ int c = Comparer<T1>.Default.Compare(Item1, other.Item1);
+ if (c != 0) return c;
+
+ c = Comparer<T2>.Default.Compare(Item2, other.Item2);
+ if (c != 0) return c;
+
+ c = Comparer<T3>.Default.Compare(Item3, other.Item3);
+ if (c != 0) return c;
+
+ c = Comparer<T4>.Default.Compare(Item4, other.Item4);
+ if (c != 0) return c;
+
+ c = Comparer<T5>.Default.Compare(Item5, other.Item5);
+ if (c != 0) return c;
+
+ return Comparer<T6>.Default.Compare(Item6, other.Item6);
+ }
+
+ int IStructuralComparable.CompareTo(object other, IComparer comparer)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4, T5, T6>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4, T5, T6>)other;
+
+ int c = comparer.Compare(Item1, objTuple.Item1);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item2, objTuple.Item2);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item3, objTuple.Item3);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item4, objTuple.Item4);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item5, objTuple.Item5);
+ if (c != 0) return c;
+
+ return comparer.Compare(Item6, objTuple.Item6);
+ }
+
+ /// <summary>
+ /// Returns the hash code for the current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance.
+ /// </summary>
+ /// <returns>A 32-bit signed integer hash code.</returns>
+ public override int GetHashCode()
+ {
+ return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
+ Item2?.GetHashCode() ?? 0,
+ Item3?.GetHashCode() ?? 0,
+ Item4?.GetHashCode() ?? 0,
+ Item5?.GetHashCode() ?? 0,
+ Item6?.GetHashCode() ?? 0);
+ }
+
+ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ private int GetHashCodeCore(IEqualityComparer comparer)
+ {
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1),
+ comparer.GetHashCode(Item2),
+ comparer.GetHashCode(Item3),
+ comparer.GetHashCode(Item4),
+ comparer.GetHashCode(Item5),
+ comparer.GetHashCode(Item6));
+ }
+
+ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ /// <summary>
+ /// Returns a string that represents the value of this <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance.
+ /// </summary>
+ /// <returns>The string representation of this <see cref="ValueTuple{T1, T2, T3, T4, T5, T6}"/> instance.</returns>
+ /// <remarks>
+ /// The string returned by this method takes the form <c>(Item1, Item2, Item3, Item4, Item5, Item6)</c>.
+ /// If any field value is <see langword="null"/>, it is represented as <see cref="String.Empty"/>.
+ /// </remarks>
+ public override string ToString()
+ {
+ return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")";
+ }
+
+ string IValueTupleInternal.ToStringEnd()
+ {
+ return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ")";
+ }
+
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int ITuple.Length => 6;
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object ITuple.this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return Item1;
+ case 1:
+ return Item2;
+ case 2:
+ return Item3;
+ case 3:
+ return Item4;
+ case 4:
+ return Item5;
+ case 5:
+ return Item6;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Represents a 7-tuple, or sentuple, as a value type.
+ /// </summary>
+ /// <typeparam name="T1">The type of the tuple's first component.</typeparam>
+ /// <typeparam name="T2">The type of the tuple's second component.</typeparam>
+ /// <typeparam name="T3">The type of the tuple's third component.</typeparam>
+ /// <typeparam name="T4">The type of the tuple's fourth component.</typeparam>
+ /// <typeparam name="T5">The type of the tuple's fifth component.</typeparam>
+ /// <typeparam name="T6">The type of the tuple's sixth component.</typeparam>
+ /// <typeparam name="T7">The type of the tuple's seventh component.</typeparam>
+ [Serializable]
+ [StructLayout(LayoutKind.Auto)]
+ public struct ValueTuple<T1, T2, T3, T4, T5, T6, T7>
+ : IEquatable<ValueTuple<T1, T2, T3, T4, T5, T6, T7>>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable<ValueTuple<T1, T2, T3, T4, T5, T6, T7>>, IValueTupleInternal, ITuple
+ {
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance's first component.
+ /// </summary>
+ public T1 Item1;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance's second component.
+ /// </summary>
+ public T2 Item2;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance's third component.
+ /// </summary>
+ public T3 Item3;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance's fourth component.
+ /// </summary>
+ public T4 Item4;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance's fifth component.
+ /// </summary>
+ public T5 Item5;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance's sixth component.
+ /// </summary>
+ public T6 Item6;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance's seventh component.
+ /// </summary>
+ public T7 Item7;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> value type.
+ /// </summary>
+ /// <param name="item1">The value of the tuple's first component.</param>
+ /// <param name="item2">The value of the tuple's second component.</param>
+ /// <param name="item3">The value of the tuple's third component.</param>
+ /// <param name="item4">The value of the tuple's fourth component.</param>
+ /// <param name="item5">The value of the tuple's fifth component.</param>
+ /// <param name="item6">The value of the tuple's sixth component.</param>
+ /// <param name="item7">The value of the tuple's seventh component.</param>
+ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
+ {
+ Item1 = item1;
+ Item2 = item2;
+ Item3 = item3;
+ Item4 = item4;
+ Item5 = item5;
+ Item6 = item6;
+ Item7 = item7;
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance is equal to a specified object.
+ /// </summary>
+ /// <param name="obj">The object to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified object; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="obj"/> parameter is considered to be equal to the current instance under the following conditions:
+ /// <list type="bullet">
+ /// <item><description>It is a <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> value type.</description></item>
+ /// <item><description>Its components are of the same types as those of the current instance.</description></item>
+ /// <item><description>Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component.</description></item>
+ /// </list>
+ /// </remarks>
+ public override bool Equals(object obj)
+ {
+ return obj is ValueTuple<T1, T2, T3, T4, T5, T6, T7> && Equals((ValueTuple<T1, T2, T3, T4, T5, T6, T7>)obj);
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/>
+ /// instance is equal to a specified <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/>.
+ /// </summary>
+ /// <param name="other">The tuple to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified tuple; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="other"/> parameter is considered to be equal to the current instance if each of its fields
+ /// are equal to that of the current instance, using the default comparer for that field's type.
+ /// </remarks>
+ public bool Equals(ValueTuple<T1, T2, T3, T4, T5, T6, T7> other)
+ {
+ return EqualityComparer<T1>.Default.Equals(Item1, other.Item1)
+ && EqualityComparer<T2>.Default.Equals(Item2, other.Item2)
+ && EqualityComparer<T3>.Default.Equals(Item3, other.Item3)
+ && EqualityComparer<T4>.Default.Equals(Item4, other.Item4)
+ && EqualityComparer<T5>.Default.Equals(Item5, other.Item5)
+ && EqualityComparer<T6>.Default.Equals(Item6, other.Item6)
+ && EqualityComparer<T7>.Default.Equals(Item7, other.Item7);
+ }
+
+ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
+ {
+ if (other == null || !(other is ValueTuple<T1, T2, T3, T4, T5, T6, T7>)) return false;
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4, T5, T6, T7>)other;
+
+ return comparer.Equals(Item1, objTuple.Item1)
+ && comparer.Equals(Item2, objTuple.Item2)
+ && comparer.Equals(Item3, objTuple.Item3)
+ && comparer.Equals(Item4, objTuple.Item4)
+ && comparer.Equals(Item5, objTuple.Item5)
+ && comparer.Equals(Item6, objTuple.Item6)
+ && comparer.Equals(Item7, objTuple.Item7);
+ }
+
+ int IComparable.CompareTo(object other)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4, T5, T6, T7>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ return CompareTo((ValueTuple<T1, T2, T3, T4, T5, T6, T7>)other);
+ }
+
+ /// <summary>Compares this instance to a specified instance and returns an indication of their relative values.</summary>
+ /// <param name="other">An instance to compare.</param>
+ /// <returns>
+ /// A signed number indicating the relative values of this instance and <paramref name="other"/>.
+ /// Returns less than zero if this instance is less than <paramref name="other"/>, zero if this
+ /// instance is equal to <paramref name="other"/>, and greater than zero if this instance is greater
+ /// than <paramref name="other"/>.
+ /// </returns>
+ public int CompareTo(ValueTuple<T1, T2, T3, T4, T5, T6, T7> other)
+ {
+ int c = Comparer<T1>.Default.Compare(Item1, other.Item1);
+ if (c != 0) return c;
+
+ c = Comparer<T2>.Default.Compare(Item2, other.Item2);
+ if (c != 0) return c;
+
+ c = Comparer<T3>.Default.Compare(Item3, other.Item3);
+ if (c != 0) return c;
+
+ c = Comparer<T4>.Default.Compare(Item4, other.Item4);
+ if (c != 0) return c;
+
+ c = Comparer<T5>.Default.Compare(Item5, other.Item5);
+ if (c != 0) return c;
+
+ c = Comparer<T6>.Default.Compare(Item6, other.Item6);
+ if (c != 0) return c;
+
+ return Comparer<T7>.Default.Compare(Item7, other.Item7);
+ }
+
+ int IStructuralComparable.CompareTo(object other, IComparer comparer)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4, T5, T6, T7>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4, T5, T6, T7>)other;
+
+ int c = comparer.Compare(Item1, objTuple.Item1);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item2, objTuple.Item2);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item3, objTuple.Item3);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item4, objTuple.Item4);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item5, objTuple.Item5);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item6, objTuple.Item6);
+ if (c != 0) return c;
+
+ return comparer.Compare(Item7, objTuple.Item7);
+ }
+
+ /// <summary>
+ /// Returns the hash code for the current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance.
+ /// </summary>
+ /// <returns>A 32-bit signed integer hash code.</returns>
+ public override int GetHashCode()
+ {
+ return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
+ Item2?.GetHashCode() ?? 0,
+ Item3?.GetHashCode() ?? 0,
+ Item4?.GetHashCode() ?? 0,
+ Item5?.GetHashCode() ?? 0,
+ Item6?.GetHashCode() ?? 0,
+ Item7?.GetHashCode() ?? 0);
+ }
+
+ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ private int GetHashCodeCore(IEqualityComparer comparer)
+ {
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1),
+ comparer.GetHashCode(Item2),
+ comparer.GetHashCode(Item3),
+ comparer.GetHashCode(Item4),
+ comparer.GetHashCode(Item5),
+ comparer.GetHashCode(Item6),
+ comparer.GetHashCode(Item7));
+ }
+
+ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ /// <summary>
+ /// Returns a string that represents the value of this <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance.
+ /// </summary>
+ /// <returns>The string representation of this <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7}"/> instance.</returns>
+ /// <remarks>
+ /// The string returned by this method takes the form <c>(Item1, Item2, Item3, Item4, Item5, Item6, Item7)</c>.
+ /// If any field value is <see langword="null"/>, it is represented as <see cref="String.Empty"/>.
+ /// </remarks>
+ public override string ToString()
+ {
+ return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")";
+ }
+
+ string IValueTupleInternal.ToStringEnd()
+ {
+ return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ")";
+ }
+
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int ITuple.Length => 7;
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object ITuple.this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return Item1;
+ case 1:
+ return Item2;
+ case 2:
+ return Item3;
+ case 3:
+ return Item4;
+ case 4:
+ return Item5;
+ case 5:
+ return Item6;
+ case 6:
+ return Item7;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Represents an 8-tuple, or octuple, as a value type.
+ /// </summary>
+ /// <typeparam name="T1">The type of the tuple's first component.</typeparam>
+ /// <typeparam name="T2">The type of the tuple's second component.</typeparam>
+ /// <typeparam name="T3">The type of the tuple's third component.</typeparam>
+ /// <typeparam name="T4">The type of the tuple's fourth component.</typeparam>
+ /// <typeparam name="T5">The type of the tuple's fifth component.</typeparam>
+ /// <typeparam name="T6">The type of the tuple's sixth component.</typeparam>
+ /// <typeparam name="T7">The type of the tuple's seventh component.</typeparam>
+ /// <typeparam name="TRest">The type of the tuple's eighth component.</typeparam>
+ [Serializable]
+ [StructLayout(LayoutKind.Auto)]
+ public struct ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>
+ : IEquatable<ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>>, IStructuralEquatable, IStructuralComparable, IComparable, IComparable<ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>>, IValueTupleInternal, ITuple
+ where TRest : struct
+ {
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance's first component.
+ /// </summary>
+ public T1 Item1;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance's second component.
+ /// </summary>
+ public T2 Item2;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance's third component.
+ /// </summary>
+ public T3 Item3;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance's fourth component.
+ /// </summary>
+ public T4 Item4;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance's fifth component.
+ /// </summary>
+ public T5 Item5;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance's sixth component.
+ /// </summary>
+ public T6 Item6;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance's seventh component.
+ /// </summary>
+ public T7 Item7;
+ /// <summary>
+ /// The current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance's eighth component.
+ /// </summary>
+ public TRest Rest;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> value type.
+ /// </summary>
+ /// <param name="item1">The value of the tuple's first component.</param>
+ /// <param name="item2">The value of the tuple's second component.</param>
+ /// <param name="item3">The value of the tuple's third component.</param>
+ /// <param name="item4">The value of the tuple's fourth component.</param>
+ /// <param name="item5">The value of the tuple's fifth component.</param>
+ /// <param name="item6">The value of the tuple's sixth component.</param>
+ /// <param name="item7">The value of the tuple's seventh component.</param>
+ /// <param name="rest">The value of the tuple's eight component.</param>
+ public ValueTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest)
+ {
+ if (!(rest is IValueTupleInternal))
+ {
+ throw new ArgumentException(SR.ArgumentException_ValueTupleLastArgumentNotAValueTuple);
+ }
+
+ Item1 = item1;
+ Item2 = item2;
+ Item3 = item3;
+ Item4 = item4;
+ Item5 = item5;
+ Item6 = item6;
+ Item7 = item7;
+ Rest = rest;
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance is equal to a specified object.
+ /// </summary>
+ /// <param name="obj">The object to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified object; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="obj"/> parameter is considered to be equal to the current instance under the following conditions:
+ /// <list type="bullet">
+ /// <item><description>It is a <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> value type.</description></item>
+ /// <item><description>Its components are of the same types as those of the current instance.</description></item>
+ /// <item><description>Its components are equal to those of the current instance. Equality is determined by the default object equality comparer for each component.</description></item>
+ /// </list>
+ /// </remarks>
+ public override bool Equals(object obj)
+ {
+ return obj is ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> && Equals((ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>)obj);
+ }
+
+ /// <summary>
+ /// Returns a value that indicates whether the current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/>
+ /// instance is equal to a specified <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/>.
+ /// </summary>
+ /// <param name="other">The tuple to compare with this instance.</param>
+ /// <returns><see langword="true"/> if the current instance is equal to the specified tuple; otherwise, <see langword="false"/>.</returns>
+ /// <remarks>
+ /// The <paramref name="other"/> parameter is considered to be equal to the current instance if each of its fields
+ /// are equal to that of the current instance, using the default comparer for that field's type.
+ /// </remarks>
+ public bool Equals(ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> other)
+ {
+ return EqualityComparer<T1>.Default.Equals(Item1, other.Item1)
+ && EqualityComparer<T2>.Default.Equals(Item2, other.Item2)
+ && EqualityComparer<T3>.Default.Equals(Item3, other.Item3)
+ && EqualityComparer<T4>.Default.Equals(Item4, other.Item4)
+ && EqualityComparer<T5>.Default.Equals(Item5, other.Item5)
+ && EqualityComparer<T6>.Default.Equals(Item6, other.Item6)
+ && EqualityComparer<T7>.Default.Equals(Item7, other.Item7)
+ && EqualityComparer<TRest>.Default.Equals(Rest, other.Rest);
+ }
+
+ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer)
+ {
+ if (other == null || !(other is ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>)) return false;
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>)other;
+
+ return comparer.Equals(Item1, objTuple.Item1)
+ && comparer.Equals(Item2, objTuple.Item2)
+ && comparer.Equals(Item3, objTuple.Item3)
+ && comparer.Equals(Item4, objTuple.Item4)
+ && comparer.Equals(Item5, objTuple.Item5)
+ && comparer.Equals(Item6, objTuple.Item6)
+ && comparer.Equals(Item7, objTuple.Item7)
+ && comparer.Equals(Rest, objTuple.Rest);
+ }
+
+ int IComparable.CompareTo(object other)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ return CompareTo((ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>)other);
+ }
+
+ /// <summary>Compares this instance to a specified instance and returns an indication of their relative values.</summary>
+ /// <param name="other">An instance to compare.</param>
+ /// <returns>
+ /// A signed number indicating the relative values of this instance and <paramref name="other"/>.
+ /// Returns less than zero if this instance is less than <paramref name="other"/>, zero if this
+ /// instance is equal to <paramref name="other"/>, and greater than zero if this instance is greater
+ /// than <paramref name="other"/>.
+ /// </returns>
+ public int CompareTo(ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> other)
+ {
+ int c = Comparer<T1>.Default.Compare(Item1, other.Item1);
+ if (c != 0) return c;
+
+ c = Comparer<T2>.Default.Compare(Item2, other.Item2);
+ if (c != 0) return c;
+
+ c = Comparer<T3>.Default.Compare(Item3, other.Item3);
+ if (c != 0) return c;
+
+ c = Comparer<T4>.Default.Compare(Item4, other.Item4);
+ if (c != 0) return c;
+
+ c = Comparer<T5>.Default.Compare(Item5, other.Item5);
+ if (c != 0) return c;
+
+ c = Comparer<T6>.Default.Compare(Item6, other.Item6);
+ if (c != 0) return c;
+
+ c = Comparer<T7>.Default.Compare(Item7, other.Item7);
+ if (c != 0) return c;
+
+ return Comparer<TRest>.Default.Compare(Rest, other.Rest);
+ }
+
+ int IStructuralComparable.CompareTo(object other, IComparer comparer)
+ {
+ if (other == null) return 1;
+
+ if (!(other is ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>))
+ {
+ throw new ArgumentException(SR.Format(SR.ArgumentException_ValueTupleIncorrectType, this.GetType().ToString()), nameof(other));
+ }
+
+ var objTuple = (ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>)other;
+
+ int c = comparer.Compare(Item1, objTuple.Item1);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item2, objTuple.Item2);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item3, objTuple.Item3);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item4, objTuple.Item4);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item5, objTuple.Item5);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item6, objTuple.Item6);
+ if (c != 0) return c;
+
+ c = comparer.Compare(Item7, objTuple.Item7);
+ if (c != 0) return c;
+
+ return comparer.Compare(Rest, objTuple.Rest);
+ }
+
+ /// <summary>
+ /// Returns the hash code for the current <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance.
+ /// </summary>
+ /// <returns>A 32-bit signed integer hash code.</returns>
+ public override int GetHashCode()
+ {
+ // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple
+ IValueTupleInternal rest = Rest as IValueTupleInternal;
+ if (rest == null)
+ {
+ return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
+ Item2?.GetHashCode() ?? 0,
+ Item3?.GetHashCode() ?? 0,
+ Item4?.GetHashCode() ?? 0,
+ Item5?.GetHashCode() ?? 0,
+ Item6?.GetHashCode() ?? 0,
+ Item7?.GetHashCode() ?? 0);
+ }
+
+ int size = rest.Length;
+ if (size >= 8) { return rest.GetHashCode(); }
+
+ // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest
+ int k = 8 - size;
+ switch (k)
+ {
+ case 1:
+ return ValueTuple.CombineHashCodes(Item7?.GetHashCode() ?? 0,
+ rest.GetHashCode());
+ case 2:
+ return ValueTuple.CombineHashCodes(Item6?.GetHashCode() ?? 0,
+ Item7?.GetHashCode() ?? 0,
+ rest.GetHashCode());
+ case 3:
+ return ValueTuple.CombineHashCodes(Item5?.GetHashCode() ?? 0,
+ Item6?.GetHashCode() ?? 0,
+ Item7?.GetHashCode() ?? 0,
+ rest.GetHashCode());
+ case 4:
+ return ValueTuple.CombineHashCodes(Item4?.GetHashCode() ?? 0,
+ Item5?.GetHashCode() ?? 0,
+ Item6?.GetHashCode() ?? 0,
+ Item7?.GetHashCode() ?? 0,
+ rest.GetHashCode());
+ case 5:
+ return ValueTuple.CombineHashCodes(Item3?.GetHashCode() ?? 0,
+ Item4?.GetHashCode() ?? 0,
+ Item5?.GetHashCode() ?? 0,
+ Item6?.GetHashCode() ?? 0,
+ Item7?.GetHashCode() ?? 0,
+ rest.GetHashCode());
+ case 6:
+ return ValueTuple.CombineHashCodes(Item2?.GetHashCode() ?? 0,
+ Item3?.GetHashCode() ?? 0,
+ Item4?.GetHashCode() ?? 0,
+ Item5?.GetHashCode() ?? 0,
+ Item6?.GetHashCode() ?? 0,
+ Item7?.GetHashCode() ?? 0,
+ rest.GetHashCode());
+ case 7:
+ case 8:
+ return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
+ Item2?.GetHashCode() ?? 0,
+ Item3?.GetHashCode() ?? 0,
+ Item4?.GetHashCode() ?? 0,
+ Item5?.GetHashCode() ?? 0,
+ Item6?.GetHashCode() ?? 0,
+ Item7?.GetHashCode() ?? 0,
+ rest.GetHashCode());
+ }
+
+ Contract.Assert(false, "Missed all cases for computing ValueTuple hash code");
+ return -1;
+ }
+
+ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ private int GetHashCodeCore(IEqualityComparer comparer)
+ {
+ // We want to have a limited hash in this case. We'll use the last 8 elements of the tuple
+ IValueTupleInternal rest = Rest as IValueTupleInternal;
+ if (rest == null)
+ {
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3),
+ comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6),
+ comparer.GetHashCode(Item7));
+ }
+
+ int size = rest.Length;
+ if (size >= 8) { return rest.GetHashCode(comparer); }
+
+ // In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest
+ int k = 8 - size;
+ switch (k)
+ {
+ case 1:
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item7), rest.GetHashCode(comparer));
+ case 2:
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer));
+ case 3:
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7),
+ rest.GetHashCode(comparer));
+ case 4:
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6),
+ comparer.GetHashCode(Item7), rest.GetHashCode(comparer));
+ case 5:
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item3), comparer.GetHashCode(Item4), comparer.GetHashCode(Item5),
+ comparer.GetHashCode(Item6), comparer.GetHashCode(Item7), rest.GetHashCode(comparer));
+ case 6:
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item2), comparer.GetHashCode(Item3), comparer.GetHashCode(Item4),
+ comparer.GetHashCode(Item5), comparer.GetHashCode(Item6), comparer.GetHashCode(Item7),
+ rest.GetHashCode(comparer));
+ case 7:
+ case 8:
+ return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1), comparer.GetHashCode(Item2), comparer.GetHashCode(Item3),
+ comparer.GetHashCode(Item4), comparer.GetHashCode(Item5), comparer.GetHashCode(Item6),
+ comparer.GetHashCode(Item7), rest.GetHashCode(comparer));
+ }
+
+ Contract.Assert(false, "Missed all cases for computing ValueTuple hash code");
+ return -1;
+ }
+
+ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
+ {
+ return GetHashCodeCore(comparer);
+ }
+
+ /// <summary>
+ /// Returns a string that represents the value of this <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance.
+ /// </summary>
+ /// <returns>The string representation of this <see cref="ValueTuple{T1, T2, T3, T4, T5, T6, T7, TRest}"/> instance.</returns>
+ /// <remarks>
+ /// The string returned by this method takes the form <c>(Item1, Item2, Item3, Item4, Item5, Item6, Item7, Rest)</c>.
+ /// If any field value is <see langword="null"/>, it is represented as <see cref="String.Empty"/>.
+ /// </remarks>
+ public override string ToString()
+ {
+ IValueTupleInternal rest = Rest as IValueTupleInternal;
+ if (rest == null)
+ {
+ return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")";
+ }
+ else
+ {
+ return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd();
+ }
+ }
+
+ string IValueTupleInternal.ToStringEnd()
+ {
+ IValueTupleInternal rest = Rest as IValueTupleInternal;
+ if (rest == null)
+ {
+ return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")";
+ }
+ else
+ {
+ return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd();
+ }
+ }
+
+ /// <summary>
+ /// The number of positions in this data structure.
+ /// </summary>
+ int ITuple.Length
+ {
+ get
+ {
+ IValueTupleInternal rest = Rest as IValueTupleInternal;
+ return rest == null ? 8 : 7 + rest.Length;
+ }
+ }
+
+ /// <summary>
+ /// Get the element at position <param name="index"/>.
+ /// </summary>
+ object ITuple.this[int index]
+ {
+ get
+ {
+ switch (index)
+ {
+ case 0:
+ return Item1;
+ case 1:
+ return Item2;
+ case 2:
+ return Item3;
+ case 3:
+ return Item4;
+ case 4:
+ return Item5;
+ case 5:
+ return Item6;
+ case 6:
+ return Item7;
+ }
+
+ IValueTupleInternal rest = Rest as IValueTupleInternal;
+ if (rest == null)
+ {
+ if (index == 7)
+ {
+ return Rest;
+ }
+ throw new IndexOutOfRangeException();
+ }
+ return rest[index - 7];
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Version.cs b/src/mscorlib/shared/System/Version.cs
new file mode 100644
index 0000000000..54b2052ddb
--- /dev/null
+++ b/src/mscorlib/shared/System/Version.cs
@@ -0,0 +1,495 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// 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.Globalization;
+using System.Diagnostics;
+using System.Diagnostics.Contracts;
+using System.Text;
+
+namespace System
+{
+ // A Version object contains four hierarchical numeric components: major, minor,
+ // build and revision. Build and revision may be unspecified, which is represented
+ // internally as a -1. By definition, an unspecified component matches anything
+ // (both unspecified and specified), and an unspecified component is "less than" any
+ // specified component.
+
+ [Serializable]
+ public sealed class Version : ICloneable, IComparable
+ , IComparable<Version>, IEquatable<Version>
+ {
+ // AssemblyName depends on the order staying the same
+ private readonly int _Major;
+ private readonly int _Minor;
+ private readonly int _Build = -1;
+ private readonly int _Revision = -1;
+
+ public Version(int major, int minor, int build, int revision)
+ {
+ if (major < 0)
+ throw new ArgumentOutOfRangeException(nameof(major), SR.ArgumentOutOfRange_Version);
+
+ if (minor < 0)
+ throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version);
+
+ if (build < 0)
+ throw new ArgumentOutOfRangeException(nameof(build), SR.ArgumentOutOfRange_Version);
+
+ if (revision < 0)
+ throw new ArgumentOutOfRangeException(nameof(revision), SR.ArgumentOutOfRange_Version);
+ Contract.EndContractBlock();
+
+ _Major = major;
+ _Minor = minor;
+ _Build = build;
+ _Revision = revision;
+ }
+
+ public Version(int major, int minor, int build)
+ {
+ if (major < 0)
+ throw new ArgumentOutOfRangeException(nameof(major), SR.ArgumentOutOfRange_Version);
+
+ if (minor < 0)
+ throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version);
+
+ if (build < 0)
+ throw new ArgumentOutOfRangeException(nameof(build), SR.ArgumentOutOfRange_Version);
+
+ Contract.EndContractBlock();
+
+ _Major = major;
+ _Minor = minor;
+ _Build = build;
+ }
+
+ public Version(int major, int minor)
+ {
+ if (major < 0)
+ throw new ArgumentOutOfRangeException(nameof(major), SR.ArgumentOutOfRange_Version);
+
+ if (minor < 0)
+ throw new ArgumentOutOfRangeException(nameof(minor), SR.ArgumentOutOfRange_Version);
+ Contract.EndContractBlock();
+
+ _Major = major;
+ _Minor = minor;
+ }
+
+ public Version(String version)
+ {
+ Version v = Version.Parse(version);
+ _Major = v.Major;
+ _Minor = v.Minor;
+ _Build = v.Build;
+ _Revision = v.Revision;
+ }
+
+ public Version()
+ {
+ _Major = 0;
+ _Minor = 0;
+ }
+
+ private Version(Version version)
+ {
+ Debug.Assert(version != null);
+
+ _Major = version._Major;
+ _Minor = version._Minor;
+ _Build = version._Build;
+ _Revision = version._Revision;
+ }
+
+ public object Clone()
+ {
+ return new Version(this);
+ }
+
+ // Properties for setting and getting version numbers
+ public int Major
+ {
+ get { return _Major; }
+ }
+
+ public int Minor
+ {
+ get { return _Minor; }
+ }
+
+ public int Build
+ {
+ get { return _Build; }
+ }
+
+ public int Revision
+ {
+ get { return _Revision; }
+ }
+
+ public short MajorRevision
+ {
+ get { return (short)(_Revision >> 16); }
+ }
+
+ public short MinorRevision
+ {
+ get { return (short)(_Revision & 0xFFFF); }
+ }
+
+ public int CompareTo(Object version)
+ {
+ if (version == null)
+ {
+ return 1;
+ }
+
+ Version v = version as Version;
+ if (v == null)
+ {
+ throw new ArgumentException(SR.Arg_MustBeVersion);
+ }
+
+ return CompareTo(v);
+ }
+
+ public int CompareTo(Version value)
+ {
+ return
+ object.ReferenceEquals(value, this) ? 0 :
+ object.ReferenceEquals(value, null) ? 1 :
+ _Major != value._Major ? (_Major > value._Major ? 1 : -1) :
+ _Minor != value._Minor ? (_Minor > value._Minor ? 1 : -1) :
+ _Build != value._Build ? (_Build > value._Build ? 1 : -1) :
+ _Revision != value._Revision ? (_Revision > value._Revision ? 1 : -1) :
+ 0;
+ }
+
+ public override bool Equals(Object obj)
+ {
+ return Equals(obj as Version);
+ }
+
+ public bool Equals(Version obj)
+ {
+ return object.ReferenceEquals(obj, this) ||
+ (!object.ReferenceEquals(obj, null) &&
+ _Major == obj._Major &&
+ _Minor == obj._Minor &&
+ _Build == obj._Build &&
+ _Revision == obj._Revision);
+ }
+
+ public override int GetHashCode()
+ {
+ // Let's assume that most version numbers will be pretty small and just
+ // OR some lower order bits together.
+
+ int accumulator = 0;
+
+ accumulator |= (_Major & 0x0000000F) << 28;
+ accumulator |= (_Minor & 0x000000FF) << 20;
+ accumulator |= (_Build & 0x000000FF) << 12;
+ accumulator |= (_Revision & 0x00000FFF);
+
+ return accumulator;
+ }
+
+ public override String ToString()
+ {
+ if (_Build == -1) return (ToString(2));
+ if (_Revision == -1) return (ToString(3));
+ return (ToString(4));
+ }
+
+ public String ToString(int fieldCount)
+ {
+ StringBuilder sb;
+ switch (fieldCount)
+ {
+ case 0:
+ return (String.Empty);
+ case 1:
+ return (_Major.ToString());
+ case 2:
+ sb = StringBuilderCache.Acquire();
+ AppendPositiveNumber(_Major, sb);
+ sb.Append('.');
+ AppendPositiveNumber(_Minor, sb);
+ return StringBuilderCache.GetStringAndRelease(sb);
+ default:
+ if (_Build == -1)
+ throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "2"), nameof(fieldCount));
+
+ if (fieldCount == 3)
+ {
+ sb = StringBuilderCache.Acquire();
+ AppendPositiveNumber(_Major, sb);
+ sb.Append('.');
+ AppendPositiveNumber(_Minor, sb);
+ sb.Append('.');
+ AppendPositiveNumber(_Build, sb);
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+
+ if (_Revision == -1)
+ throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "3"), nameof(fieldCount));
+
+ if (fieldCount == 4)
+ {
+ sb = StringBuilderCache.Acquire();
+ AppendPositiveNumber(_Major, sb);
+ sb.Append('.');
+ AppendPositiveNumber(_Minor, sb);
+ sb.Append('.');
+ AppendPositiveNumber(_Build, sb);
+ sb.Append('.');
+ AppendPositiveNumber(_Revision, sb);
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+
+ throw new ArgumentException(SR.Format(SR.ArgumentOutOfRange_Bounds_Lower_Upper, "0", "4"), nameof(fieldCount));
+ }
+ }
+
+ //
+ // AppendPositiveNumber is an optimization to append a number to a StringBuilder object without
+ // doing any boxing and not even creating intermediate string.
+ // Note: as we always have positive numbers then it is safe to convert the number to string
+ // regardless of the current culture as we'll not have any punctuation marks in the number
+ //
+ private const int ZERO_CHAR_VALUE = (int)'0';
+ private static void AppendPositiveNumber(int num, StringBuilder sb)
+ {
+ Debug.Assert(num >= 0, "AppendPositiveNumber expect positive numbers");
+
+ int index = sb.Length;
+ int reminder;
+
+ do
+ {
+ reminder = num % 10;
+ num = num / 10;
+ sb.Insert(index, (char)(ZERO_CHAR_VALUE + reminder));
+ } while (num > 0);
+ }
+
+ public static Version Parse(string input)
+ {
+ if (input == null)
+ {
+ throw new ArgumentNullException(nameof(input));
+ }
+ Contract.EndContractBlock();
+
+ VersionResult r = new VersionResult();
+ r.Init(nameof(input), true);
+ if (!TryParseVersion(input, ref r))
+ {
+ throw r.GetVersionParseException();
+ }
+ return r.m_parsedVersion;
+ }
+
+ public static bool TryParse(string input, out Version result)
+ {
+ VersionResult r = new VersionResult();
+ r.Init(nameof(input), false);
+ bool b = TryParseVersion(input, ref r);
+ result = r.m_parsedVersion;
+ return b;
+ }
+
+ private static bool TryParseVersion(string version, ref VersionResult result)
+ {
+ int major, minor, build, revision;
+
+ if ((Object)version == null)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentNullException);
+ return false;
+ }
+
+ String[] parsedComponents = version.Split('.');
+ int parsedComponentsLength = parsedComponents.Length;
+ if ((parsedComponentsLength < 2) || (parsedComponentsLength > 4))
+ {
+ result.SetFailure(ParseFailureKind.ArgumentException);
+ return false;
+ }
+
+ if (!TryParseComponent(parsedComponents[0], nameof(version), ref result, out major))
+ {
+ return false;
+ }
+
+ if (!TryParseComponent(parsedComponents[1], nameof(version), ref result, out minor))
+ {
+ return false;
+ }
+
+ parsedComponentsLength -= 2;
+
+ if (parsedComponentsLength > 0)
+ {
+ if (!TryParseComponent(parsedComponents[2], "build", ref result, out build))
+ {
+ return false;
+ }
+
+ parsedComponentsLength--;
+
+ if (parsedComponentsLength > 0)
+ {
+ if (!TryParseComponent(parsedComponents[3], "revision", ref result, out revision))
+ {
+ return false;
+ }
+ else
+ {
+ result.m_parsedVersion = new Version(major, minor, build, revision);
+ }
+ }
+ else
+ {
+ result.m_parsedVersion = new Version(major, minor, build);
+ }
+ }
+ else
+ {
+ result.m_parsedVersion = new Version(major, minor);
+ }
+
+ return true;
+ }
+
+ private static bool TryParseComponent(string component, string componentName, ref VersionResult result, out int parsedComponent)
+ {
+ if (!Int32.TryParse(component, NumberStyles.Integer, CultureInfo.InvariantCulture, out parsedComponent))
+ {
+ result.SetFailure(ParseFailureKind.FormatException, component);
+ return false;
+ }
+
+ if (parsedComponent < 0)
+ {
+ result.SetFailure(ParseFailureKind.ArgumentOutOfRangeException, componentName);
+ return false;
+ }
+
+ return true;
+ }
+
+ public static bool operator ==(Version v1, Version v2)
+ {
+ if (Object.ReferenceEquals(v1, null))
+ {
+ return Object.ReferenceEquals(v2, null);
+ }
+
+ return v1.Equals(v2);
+ }
+
+ public static bool operator !=(Version v1, Version v2)
+ {
+ return !(v1 == v2);
+ }
+
+ public static bool operator <(Version v1, Version v2)
+ {
+ if ((Object)v1 == null)
+ throw new ArgumentNullException(nameof(v1));
+ Contract.EndContractBlock();
+ return (v1.CompareTo(v2) < 0);
+ }
+
+ public static bool operator <=(Version v1, Version v2)
+ {
+ if ((Object)v1 == null)
+ throw new ArgumentNullException(nameof(v1));
+ Contract.EndContractBlock();
+ return (v1.CompareTo(v2) <= 0);
+ }
+
+ public static bool operator >(Version v1, Version v2)
+ {
+ return (v2 < v1);
+ }
+
+ public static bool operator >=(Version v1, Version v2)
+ {
+ return (v2 <= v1);
+ }
+
+ internal enum ParseFailureKind
+ {
+ ArgumentNullException,
+ ArgumentException,
+ ArgumentOutOfRangeException,
+ FormatException
+ }
+
+ internal struct VersionResult
+ {
+ internal Version m_parsedVersion;
+ internal ParseFailureKind m_failure;
+ internal string m_exceptionArgument;
+ internal string m_argumentName;
+ internal bool m_canThrow;
+
+ internal void Init(string argumentName, bool canThrow)
+ {
+ m_canThrow = canThrow;
+ m_argumentName = argumentName;
+ }
+
+ internal void SetFailure(ParseFailureKind failure)
+ {
+ SetFailure(failure, String.Empty);
+ }
+
+ internal void SetFailure(ParseFailureKind failure, string argument)
+ {
+ m_failure = failure;
+ m_exceptionArgument = argument;
+ if (m_canThrow)
+ {
+ throw GetVersionParseException();
+ }
+ }
+
+ internal Exception GetVersionParseException()
+ {
+ switch (m_failure)
+ {
+ case ParseFailureKind.ArgumentNullException:
+ return new ArgumentNullException(m_argumentName);
+ case ParseFailureKind.ArgumentException:
+ return new ArgumentException(SR.Arg_VersionString);
+ case ParseFailureKind.ArgumentOutOfRangeException:
+ return new ArgumentOutOfRangeException(m_exceptionArgument, SR.ArgumentOutOfRange_Version);
+ case ParseFailureKind.FormatException:
+ // Regenerate the FormatException as would be thrown by Int32.Parse()
+ try
+ {
+ Int32.Parse(m_exceptionArgument, CultureInfo.InvariantCulture);
+ }
+ catch (FormatException e)
+ {
+ return e;
+ }
+ catch (OverflowException e)
+ {
+ return e;
+ }
+ Debug.Assert(false, "Int32.Parse() did not throw exception but TryParse failed: " + m_exceptionArgument);
+ return new FormatException(SR.Format_InvalidString);
+ default:
+ Debug.Assert(false, "Unmatched case in Version.GetVersionParseException() for value: " + m_failure);
+ return new ArgumentException(SR.Arg_VersionString);
+ }
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/shared/System/Void.cs b/src/mscorlib/shared/System/Void.cs
new file mode 100644
index 0000000000..5c20f634fc
--- /dev/null
+++ b/src/mscorlib/shared/System/Void.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+////////////////////////////////////////////////////////////////////////////////
+// Void
+// This class represents the void return type
+////////////////////////////////////////////////////////////////////////////////
+
+namespace System
+{
+ // This class represents the void return type
+ [Serializable]
+ public struct Void
+ {
+ }
+}