From 459186936496a692d0eb01c3cc1e35fad31b2271 Mon Sep 17 00:00:00 2001 From: Anipik Date: Sat, 10 Feb 2018 21:57:28 -0800 Subject: [PATCH 1/9] String Create options overlaod --- src/mscorlib/shared/System/StringComparer.cs | 25 +++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs index 73c013599da6..0a19b3d0e586 100644 --- a/src/mscorlib/shared/System/StringComparer.cs +++ b/src/mscorlib/shared/System/StringComparer.cs @@ -98,6 +98,15 @@ public static StringComparer Create(CultureInfo culture, bool ignoreCase) return new CultureAwareComparer(culture, ignoreCase); } + public static StringComparer Create(CultureInfo culture, CompareOptions options) + { + if (culture == null) + { + throw new ArgumentException(nameof(culture)); + } + return new CultureAwareComparer(culture, options); + } + public int Compare(object x, object y) { if (x == y) return 0; @@ -167,14 +176,28 @@ public sealed class CultureAwareComparer : StringComparer { private readonly CompareInfo _compareInfo; // Do not rename (binary serialization) private readonly bool _ignoreCase; // Do not rename (binary serialization) + private CompareOptions _compareOptions = CompareOptions.None; internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) { _compareInfo = culture.CompareInfo; _ignoreCase = ignoreCase; + _compareOptions = _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; } - private CompareOptions Options => _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; + internal CultureAwareComparer(CultureInfo culture, CompareOptions compareOptions) + { + _compareInfo = culture.CompareInfo; + _compareOptions = compareOptions; + } + + private CompareOptions Options + { + get + { + return _compareOptions; + } + } public override int Compare(string x, string y) { From 7606c3c52892df6ddc12d9457221f2a994f08a99 Mon Sep 17 00:00:00 2001 From: Anipik Date: Sun, 11 Feb 2018 16:41:59 -0800 Subject: [PATCH 2/9] Minor Change --- src/mscorlib/shared/System/StringComparer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs index 0a19b3d0e586..6a7c97950d7c 100644 --- a/src/mscorlib/shared/System/StringComparer.cs +++ b/src/mscorlib/shared/System/StringComparer.cs @@ -176,7 +176,7 @@ public sealed class CultureAwareComparer : StringComparer { private readonly CompareInfo _compareInfo; // Do not rename (binary serialization) private readonly bool _ignoreCase; // Do not rename (binary serialization) - private CompareOptions _compareOptions = CompareOptions.None; + private CompareOptions _compareOptions; internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) { From aa21f4336df3bc1d42240a16309c9009bc51d2a0 Mon Sep 17 00:00:00 2001 From: Anipik Date: Mon, 12 Feb 2018 14:38:43 -0800 Subject: [PATCH 3/9] Feedback --- src/mscorlib/shared/System/StringComparer.cs | 34 +++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs index 6a7c97950d7c..2bea843a8dfd 100644 --- a/src/mscorlib/shared/System/StringComparer.cs +++ b/src/mscorlib/shared/System/StringComparer.cs @@ -13,8 +13,8 @@ namespace System [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract class StringComparer : IComparer, IEqualityComparer, IComparer, IEqualityComparer { - 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 CultureAwareComparer s_invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.None); + private static readonly CultureAwareComparer s_invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.IgnoreCase); private static readonly OrdinalCaseSensitiveComparer s_ordinal = new OrdinalCaseSensitiveComparer(); private static readonly OrdinalIgnoreCaseComparer s_ordinalIgnoreCase = new OrdinalIgnoreCaseComparer(); @@ -38,7 +38,7 @@ public static StringComparer CurrentCulture { get { - return new CultureAwareComparer(CultureInfo.CurrentCulture, false); + return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.None); } } @@ -46,7 +46,7 @@ public static StringComparer CurrentCultureIgnoreCase { get { - return new CultureAwareComparer(CultureInfo.CurrentCulture, true); + return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.IgnoreCase); } } @@ -95,7 +95,7 @@ public static StringComparer Create(CultureInfo culture, bool ignoreCase) throw new ArgumentNullException(nameof(culture)); } - return new CultureAwareComparer(culture, ignoreCase); + return new CultureAwareComparer(culture, CompareOptions.IgnoreCase); } public static StringComparer Create(CultureInfo culture, CompareOptions options) @@ -104,6 +104,7 @@ public static StringComparer Create(CultureInfo culture, CompareOptions options) { throw new ArgumentException(nameof(culture)); } + return new CultureAwareComparer(culture, options); } @@ -176,27 +177,28 @@ public sealed class CultureAwareComparer : StringComparer { private readonly CompareInfo _compareInfo; // Do not rename (binary serialization) private readonly bool _ignoreCase; // Do not rename (binary serialization) - private CompareOptions _compareOptions; + [OptionalField] + private CompareOptions _options; + + [Obsolete] internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) { _compareInfo = culture.CompareInfo; _ignoreCase = ignoreCase; - _compareOptions = _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; + _options = _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; } internal CultureAwareComparer(CultureInfo culture, CompareOptions compareOptions) { _compareInfo = culture.CompareInfo; - _compareOptions = compareOptions; + _options = compareOptions; } - private CompareOptions Options + [OnDeserialized] + private void OnDeserializedMethod(StreamingContext context) { - get - { - return _compareOptions; - } + _options |= _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; } public override int Compare(string x, string y) @@ -204,14 +206,14 @@ 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); + 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; + return _compareInfo.Compare(x, y, _options) == 0; } public override int GetHashCode(string obj) @@ -220,7 +222,7 @@ public override int GetHashCode(string obj) { throw new ArgumentNullException(nameof(obj)); } - return _compareInfo.GetHashCodeOfString(obj, Options); + return _compareInfo.GetHashCodeOfString(obj, _options); } // Equals method for the comparer itself. From 29046803801b6ffd235cadbb2bd2469148c96ee5 Mon Sep 17 00:00:00 2001 From: Anipik Date: Mon, 12 Feb 2018 14:43:07 -0800 Subject: [PATCH 4/9] validation on options --- src/mscorlib/shared/System/StringComparer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs index 2bea843a8dfd..fca026d75233 100644 --- a/src/mscorlib/shared/System/StringComparer.cs +++ b/src/mscorlib/shared/System/StringComparer.cs @@ -105,6 +105,11 @@ public static StringComparer Create(CultureInfo culture, CompareOptions options) throw new ArgumentException(nameof(culture)); } + if (Enum.IsDefined(typeof(CompareOptions), options)) + { + throw new ArgumentException(nameof(options)); + } + return new CultureAwareComparer(culture, options); } From 23acf6c01bfba82b2938e5522e93087f62d59fe1 Mon Sep 17 00:00:00 2001 From: Anipik Date: Mon, 12 Feb 2018 15:44:58 -0800 Subject: [PATCH 5/9] obsolete removed --- src/mscorlib/shared/System/StringComparer.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs index fca026d75233..f5519d0bf03a 100644 --- a/src/mscorlib/shared/System/StringComparer.cs +++ b/src/mscorlib/shared/System/StringComparer.cs @@ -13,8 +13,8 @@ namespace System [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract class StringComparer : IComparer, IEqualityComparer, IComparer, IEqualityComparer { - private static readonly CultureAwareComparer s_invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.None); - private static readonly CultureAwareComparer s_invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.IgnoreCase); + 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 OrdinalCaseSensitiveComparer s_ordinal = new OrdinalCaseSensitiveComparer(); private static readonly OrdinalIgnoreCaseComparer s_ordinalIgnoreCase = new OrdinalIgnoreCaseComparer(); @@ -38,7 +38,7 @@ public static StringComparer CurrentCulture { get { - return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.None); + return new CultureAwareComparer(CultureInfo.CurrentCulture, false); } } @@ -46,7 +46,7 @@ public static StringComparer CurrentCultureIgnoreCase { get { - return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.IgnoreCase); + return new CultureAwareComparer(CultureInfo.CurrentCulture, true); } } @@ -95,7 +95,7 @@ public static StringComparer Create(CultureInfo culture, bool ignoreCase) throw new ArgumentNullException(nameof(culture)); } - return new CultureAwareComparer(culture, CompareOptions.IgnoreCase); + return new CultureAwareComparer(culture, ignoreCase); } public static StringComparer Create(CultureInfo culture, CompareOptions options) @@ -105,7 +105,7 @@ public static StringComparer Create(CultureInfo culture, CompareOptions options) throw new ArgumentException(nameof(culture)); } - if (Enum.IsDefined(typeof(CompareOptions), options)) + if (!Enum.IsDefined(typeof(CompareOptions), options)) { throw new ArgumentException(nameof(options)); } @@ -186,7 +186,6 @@ public sealed class CultureAwareComparer : StringComparer [OptionalField] private CompareOptions _options; - [Obsolete] internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) { _compareInfo = culture.CompareInfo; From cbb560a3aadbff535c8dc3b456f0641f2570c7b0 Mon Sep 17 00:00:00 2001 From: Anipik Date: Tue, 13 Feb 2018 00:20:44 -0800 Subject: [PATCH 6/9] Implementing Iserializable and removing ignorecase --- src/mscorlib/shared/System/StringComparer.cs | 39 +++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs index f5519d0bf03a..503658bce4fc 100644 --- a/src/mscorlib/shared/System/StringComparer.cs +++ b/src/mscorlib/shared/System/StringComparer.cs @@ -105,9 +105,9 @@ public static StringComparer Create(CultureInfo culture, CompareOptions options) throw new ArgumentException(nameof(culture)); } - if (!Enum.IsDefined(typeof(CompareOptions), options)) + if ((options & CultureAwareComparer.ValidCompareMaskOffFlags) != 0) { - throw new ArgumentException(nameof(options)); + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); } return new CultureAwareComparer(culture, options); @@ -138,7 +138,6 @@ public int Compare(object x, object y) throw new ArgumentException(SR.Argument_ImplementIComparable); } - public new bool Equals(Object x, Object y) { if (x == y) return true; @@ -178,19 +177,19 @@ public int GetHashCode(object obj) [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public sealed class CultureAwareComparer : StringComparer + public sealed class CultureAwareComparer : StringComparer, ISerializable { + internal const CompareOptions ValidCompareMaskOffFlags = ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort); + private readonly CompareInfo _compareInfo; // Do not rename (binary serialization) - private readonly bool _ignoreCase; // Do not rename (binary serialization) [OptionalField] private CompareOptions _options; - + internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) { _compareInfo = culture.CompareInfo; - _ignoreCase = ignoreCase; - _options = _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; + _options = ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; } internal CultureAwareComparer(CultureInfo culture, CompareOptions compareOptions) @@ -199,10 +198,17 @@ internal CultureAwareComparer(CultureInfo culture, CompareOptions compareOptions _options = compareOptions; } - [OnDeserialized] - private void OnDeserializedMethod(StreamingContext context) + internal CultureAwareComparer(SerializationInfo info, StreamingContext context) { - _options |= _ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; + _compareInfo = (CompareInfo)info.GetValue("_compareInfo", typeof(CompareInfo)); + bool ignoreCase = info.GetBoolean("ignoreCase"); + + var obj = info.GetValueNoThrow("_options", typeof(CompareOptions)); + if (obj != null) + _options = (CompareOptions)obj; + + // fix up the _options value in case we are getting old serialized object not having _options + _options |= ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; } public override int Compare(string x, string y) @@ -235,14 +241,21 @@ public override bool Equals(object obj) CultureAwareComparer comparer = obj as CultureAwareComparer; return comparer != null && - _ignoreCase == comparer._ignoreCase && + _options == comparer._options && _compareInfo.Equals(comparer._compareInfo); } public override int GetHashCode() { int hashCode = _compareInfo.GetHashCode(); - return _ignoreCase ? ~hashCode : hashCode; + return (_options & CompareOptions.IgnoreCase) != 0 ? ~hashCode : hashCode; + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("_compareInfo", _compareInfo); + info.AddValue("_options", _options); + info.AddValue("_ignoreCase", (_options & CompareOptions.IgnoreCase) != 0); } } From d279555d90768b5accb9f89a37bce850172a332a Mon Sep 17 00:00:00 2001 From: Anipik Date: Tue, 13 Feb 2018 11:21:43 -0800 Subject: [PATCH 7/9] HashCode and serialization changes --- src/mscorlib/shared/System/StringComparer.cs | 38 +++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs index 503658bce4fc..2770a30afbe3 100644 --- a/src/mscorlib/shared/System/StringComparer.cs +++ b/src/mscorlib/shared/System/StringComparer.cs @@ -13,8 +13,8 @@ namespace System [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract class StringComparer : IComparer, IEqualityComparer, IComparer, IEqualityComparer { - 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 CultureAwareComparer s_invariantCulture = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.None); + private static readonly CultureAwareComparer s_invariantCultureIgnoreCase = new CultureAwareComparer(CultureInfo.InvariantCulture, CompareOptions.IgnoreCase); private static readonly OrdinalCaseSensitiveComparer s_ordinal = new OrdinalCaseSensitiveComparer(); private static readonly OrdinalIgnoreCaseComparer s_ordinalIgnoreCase = new OrdinalIgnoreCaseComparer(); @@ -38,7 +38,7 @@ public static StringComparer CurrentCulture { get { - return new CultureAwareComparer(CultureInfo.CurrentCulture, false); + return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.None); } } @@ -46,7 +46,7 @@ public static StringComparer CurrentCultureIgnoreCase { get { - return new CultureAwareComparer(CultureInfo.CurrentCulture, true); + return new CultureAwareComparer(CultureInfo.CurrentCulture, CompareOptions.IgnoreCase); } } @@ -95,7 +95,10 @@ public static StringComparer Create(CultureInfo culture, bool ignoreCase) throw new ArgumentNullException(nameof(culture)); } - return new CultureAwareComparer(culture, ignoreCase); + if (ignoreCase) + return new CultureAwareComparer(culture, CompareOptions.IgnoreCase); + + return new CultureAwareComparer(culture, CompareOptions.None); } public static StringComparer Create(CultureInfo culture, CompareOptions options) @@ -105,11 +108,6 @@ public static StringComparer Create(CultureInfo culture, CompareOptions options) throw new ArgumentException(nameof(culture)); } - if ((options & CultureAwareComparer.ValidCompareMaskOffFlags) != 0) - { - throw new ArgumentException(SR.Argument_InvalidFlag, nameof(options)); - } - return new CultureAwareComparer(culture, options); } @@ -179,26 +177,23 @@ public int GetHashCode(object obj) [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public sealed class CultureAwareComparer : StringComparer, ISerializable { - internal const CompareOptions ValidCompareMaskOffFlags = ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort); + private const CompareOptions ValidCompareMaskOffFlags = ~(CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType | CompareOptions.StringSort); private readonly CompareInfo _compareInfo; // Do not rename (binary serialization) - - [OptionalField] private CompareOptions _options; - - internal CultureAwareComparer(CultureInfo culture, bool ignoreCase) - { - _compareInfo = culture.CompareInfo; - _options = ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None; - } internal CultureAwareComparer(CultureInfo culture, CompareOptions compareOptions) { _compareInfo = culture.CompareInfo; + + if ((compareOptions & CultureAwareComparer.ValidCompareMaskOffFlags) != 0) + { + throw new ArgumentException(SR.Argument_InvalidFlag, nameof(compareOptions)); + } _options = compareOptions; } - internal CultureAwareComparer(SerializationInfo info, StreamingContext context) + private CultureAwareComparer(SerializationInfo info, StreamingContext context) { _compareInfo = (CompareInfo)info.GetValue("_compareInfo", typeof(CompareInfo)); bool ignoreCase = info.GetBoolean("ignoreCase"); @@ -247,8 +242,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - int hashCode = _compareInfo.GetHashCode(); - return (_options & CompareOptions.IgnoreCase) != 0 ? ~hashCode : hashCode; + return _compareInfo.GetHashCode() ^ ((int)_options & 0x7FFFFFFF); } public void GetObjectData(SerializationInfo info, StreamingContext context) From f14320568e9d4a4b6704279fb07ad227fefcd8b8 Mon Sep 17 00:00:00 2001 From: Anipik Date: Tue, 13 Feb 2018 12:34:51 -0800 Subject: [PATCH 8/9] made inline --- src/mscorlib/shared/System/StringComparer.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs index 2770a30afbe3..9e4977f245ef 100644 --- a/src/mscorlib/shared/System/StringComparer.cs +++ b/src/mscorlib/shared/System/StringComparer.cs @@ -94,11 +94,8 @@ public static StringComparer Create(CultureInfo culture, bool ignoreCase) { throw new ArgumentNullException(nameof(culture)); } - - if (ignoreCase) - return new CultureAwareComparer(culture, CompareOptions.IgnoreCase); - - return new CultureAwareComparer(culture, CompareOptions.None); + + return new CultureAwareComparer(culture, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None); } public static StringComparer Create(CultureInfo culture, CompareOptions options) @@ -196,7 +193,7 @@ internal CultureAwareComparer(CultureInfo culture, CompareOptions compareOptions private CultureAwareComparer(SerializationInfo info, StreamingContext context) { _compareInfo = (CompareInfo)info.GetValue("_compareInfo", typeof(CompareInfo)); - bool ignoreCase = info.GetBoolean("ignoreCase"); + bool ignoreCase = info.GetBoolean("_ignoreCase"); var obj = info.GetValueNoThrow("_options", typeof(CompareOptions)); if (obj != null) From d592c7aafed03eba56f8a1e299b1897254a18618 Mon Sep 17 00:00:00 2001 From: Anipik Date: Wed, 14 Feb 2018 10:08:34 -0800 Subject: [PATCH 9/9] Space Corrected --- src/mscorlib/shared/System/StringComparer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mscorlib/shared/System/StringComparer.cs b/src/mscorlib/shared/System/StringComparer.cs index 9e4977f245ef..b5f20c9570b5 100644 --- a/src/mscorlib/shared/System/StringComparer.cs +++ b/src/mscorlib/shared/System/StringComparer.cs @@ -95,7 +95,7 @@ public static StringComparer Create(CultureInfo culture, bool ignoreCase) throw new ArgumentNullException(nameof(culture)); } - return new CultureAwareComparer(culture, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None); + return new CultureAwareComparer(culture, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None); } public static StringComparer Create(CultureInfo culture, CompareOptions options)