diff --git a/src/Microsoft.Extensions.Primitives/StringValues.cs b/src/Microsoft.Extensions.Primitives/StringValues.cs
index 68a2d13a369..7078720df63 100644
--- a/src/Microsoft.Extensions.Primitives/StringValues.cs
+++ b/src/Microsoft.Extensions.Primitives/StringValues.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Text;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Extensions.Internal;
@@ -11,26 +12,66 @@ namespace Microsoft.Extensions.Primitives
///
/// Represents zero/null, one, or many strings in an efficient way.
///
- public struct StringValues : IList, IReadOnlyList, IEquatable, IEquatable, IEquatable
+ public class StringValues : IList, IReadOnlyList, IEquatable, IEquatable, IEquatable
{
private static readonly string[] EmptyArray = new string[0];
public static readonly StringValues Empty = new StringValues(EmptyArray);
private readonly string _value;
private readonly string[] _values;
+ private readonly Encoding _encoding;
+ private readonly byte[] _bytes;
public StringValues(string value)
{
_value = value;
- _values = null;
}
public StringValues(string[] values)
{
- _value = null;
_values = values;
}
+ private StringValues(string value, Encoding encoding)
+ {
+ _value = value;
+ _encoding = encoding;
+ _bytes = _encoding.GetBytes(value);
+ }
+
+ ///
+ /// Creates a new instance of with one string and compute its encoded bytes with the specified Encoding.
+ ///
+ /// The string to be encoded.
+ /// The to be use when computing pre-encoded bytes.
+ /// A which contains the pre-encoded bytes.
+ public static StringValues CreatePreEncoded(string value, Encoding encoding)
+ {
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+ if (encoding == null)
+ {
+ throw new ArgumentNullException(nameof(encoding));
+ }
+
+ return new StringValues(value, encoding);
+ }
+
+ ///
+ /// Try to retrieve the pre-encoded bytes of the . The return value indicates whether pre-encoded bytes exist.
+ ///
+ /// If successful, contains the pre-encoded bytes of the .
+ /// If successful, contains the used to compute the pre-encoded bytes.
+ /// true if contains pre-encoded bytes, otherwise false.
+ public bool TryGetPreEncoded(out byte[] bytes, out Encoding encoding)
+ {
+ bytes = _bytes;
+ encoding = _encoding;
+ return _bytes != null;
+ }
+
public static implicit operator StringValues(string value)
{
return new StringValues(value);
@@ -206,7 +247,7 @@ void ICollection.Clear()
public Enumerator GetEnumerator()
{
- return new Enumerator(ref this);
+ return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
@@ -424,7 +465,7 @@ public struct Enumerator : IEnumerator
private string _current;
private int _index;
- public Enumerator(ref StringValues values)
+ public Enumerator(StringValues values)
{
_values = values._values;
_current = values._value;
diff --git a/test/Microsoft.Extensions.Primitives.Tests/StringValuesTests.cs b/test/Microsoft.Extensions.Primitives.Tests/StringValuesTests.cs
index a0299065c81..0b580303335 100644
--- a/test/Microsoft.Extensions.Primitives.Tests/StringValuesTests.cs
+++ b/test/Microsoft.Extensions.Primitives.Tests/StringValuesTests.cs
@@ -5,6 +5,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using Xunit;
namespace Microsoft.Extensions.Primitives
@@ -17,7 +18,6 @@ public static TheoryData DefaultOrNullStringValues
{
return new TheoryData
{
- new StringValues(),
new StringValues((string)null),
new StringValues((string[])null),
(string)null,
@@ -63,7 +63,6 @@ public static TheoryData FilledStringValuesWithExpectedStr
{
return new TheoryData
{
- { default(StringValues), (string)null },
{ StringValues.Empty, (string)null },
{ new StringValues(new string[] { }), (string)null },
{ new StringValues(string.Empty), string.Empty },
@@ -79,7 +78,6 @@ public static TheoryData FilledStringValuesWithExpectedObj
{
return new TheoryData
{
- { default(StringValues), (object)null },
{ StringValues.Empty, (object)null },
{ new StringValues(new string[] { }), (object)null },
{ new StringValues("abc"), (object)"abc" },
@@ -96,7 +94,6 @@ public static TheoryData FilledStringValuesWithExpected
{
return new TheoryData
{
- { default(StringValues), new string[0] },
{ StringValues.Empty, new string[0] },
{ new StringValues(string.Empty), new[] { string.Empty } },
{ new StringValues("abc"), new[] { "abc" } },
@@ -333,8 +330,6 @@ public void DefaultNullOrEmpty_Concat(StringValues stringValues)
string[] empty = new string[0];
Assert.Equal(empty, StringValues.Concat(stringValues, StringValues.Empty));
Assert.Equal(empty, StringValues.Concat(StringValues.Empty, stringValues));
- Assert.Equal(empty, StringValues.Concat(stringValues, new StringValues()));
- Assert.Equal(empty, StringValues.Concat(new StringValues(), stringValues));
}
[Theory]
@@ -455,5 +450,71 @@ public void Equals_StringArray(StringValues stringValues, string[] expected)
Assert.True(StringValues.Equals(stringValues, expected));
Assert.False(StringValues.Equals(stringValues, notEqual));
}
+
+ [Theory]
+ [MemberData(nameof(DefaultOrNullStringValues))]
+ [MemberData(nameof(EmptyStringValues))]
+ [MemberData(nameof(FilledStringValues))]
+ public void TryGetConstant_NullDefaultEncodingAndEncodedBytes(StringValues stringValues)
+ {
+ Encoding encoding;
+ byte[] bytes;
+
+ Assert.False(stringValues.TryGetPreEncoded(out bytes, out encoding));
+ Assert.Null(encoding);
+ Assert.Null(bytes);
+ }
+
+ [Theory]
+ [MemberData(nameof(DefaultOrNullStringValues))]
+ [MemberData(nameof(EmptyStringValues))]
+ public void CreateConstant_ThrowsForNullOrEmpty(StringValues stringValues)
+ {
+ Assert.Throws(() => (StringValues.CreatePreEncoded(stringValues, Encoding.ASCII)));
+ }
+
+ [Fact]
+ public void CreateConstant_EncodesEmptyString()
+ {
+ var stringValueConstant = StringValues.CreatePreEncoded("", Encoding.ASCII);
+ byte[] bytes;
+ Encoding encoding;
+
+ stringValueConstant.TryGetPreEncoded(out bytes, out encoding);
+
+ Assert.NotNull(bytes);
+ Assert.Empty(bytes);
+ }
+
+ [Fact]
+ public void CreateConstant_EncodesUsingSpecifiedEncoding()
+ {
+ var testString = "foo bar";
+ var expectedBytes = Encoding.ASCII.GetBytes(testString);
+ byte[] outBytes;
+ Encoding outEncoding;
+
+ var constantStringValues = StringValues.CreatePreEncoded(testString, Encoding.ASCII);
+ constantStringValues.TryGetPreEncoded(out outBytes, out outEncoding);
+
+ Assert.Equal(expectedBytes, outBytes);
+ Assert.Equal(Encoding.ASCII, outEncoding);
+ }
+
+ [Fact]
+ public void TryGetConstant_ReturnsIdenticalBytesAndEncoding()
+ {
+ var encoding = Encoding.GetEncoding(20127); // US-ASCII
+ var constantStringValues = StringValues.CreatePreEncoded("foo bar", encoding);
+
+ byte[] bytes1, bytes2;
+ Encoding encoding1, encoding2;
+ constantStringValues.TryGetPreEncoded(out bytes1, out encoding1);
+ constantStringValues.TryGetPreEncoded(out bytes2, out encoding2);
+
+ Assert.Same(bytes1, bytes2);
+ Assert.Same(encoding, encoding1);
+ Assert.Same(encoding, encoding2);
+ }
}
}