Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 2296948

Browse files
committed
Strip BOM when converting From UTF8 or deserializing C# string
1 parent f6ff873 commit 2296948

File tree

8 files changed

+51
-8
lines changed

8 files changed

+51
-8
lines changed

src/ServiceStack.Memory/NetCoreMemory.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,19 @@ public override ReadOnlyMemory<byte> ToUtf8(ReadOnlySpan<char> source)
177177

178178
public override ReadOnlyMemory<char> FromUtf8(ReadOnlySpan<byte> source)
179179
{
180+
source = source.WithoutBom();
180181
Memory<char> chars = new char[Encoding.UTF8.GetCharCount(source)];
181182
var charsWritten = Encoding.UTF8.GetChars(source, chars.Span);
182183
return chars.Slice(0, charsWritten);
183184
}
184185

185186
public override int ToUtf8(ReadOnlySpan<char> source, Span<byte> destination) => Encoding.UTF8.GetBytes(source, destination);
186187

187-
public override int FromUtf8(ReadOnlySpan<byte> source, Span<char> destination) => Encoding.UTF8.GetChars(source, destination);
188+
public override int FromUtf8(ReadOnlySpan<byte> source, Span<char> destination) => Encoding.UTF8.GetChars(source.WithoutBom(), destination);
188189

189190
public override byte[] ToUtf8Bytes(ReadOnlySpan<char> source) => ToUtf8(source).ToArray();
190191

191-
public override string FromUtf8Bytes(ReadOnlySpan<byte> source) => FromUtf8(source).ToString();
192+
public override string FromUtf8Bytes(ReadOnlySpan<byte> source) => FromUtf8(source.WithoutBom()).ToString();
192193

193194
public override MemoryStream ToMemoryStream(ReadOnlySpan<byte> source)
194195
{

src/ServiceStack.Text/DefaultMemory.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ public override ReadOnlyMemory<byte> ToUtf8(ReadOnlySpan<char> source)
532532

533533
public override ReadOnlyMemory<char> FromUtf8(ReadOnlySpan<byte> source)
534534
{
535-
var bytes = source.ToArray();
535+
var bytes = source.WithoutBom().ToArray();
536536
var chars = new char[Encoding.UTF8.GetCharCount(bytes)];
537537
var charsWritten = Encoding.UTF8.GetChars(bytes, 0, source.Length, chars, 0);
538538
return new ReadOnlyMemory<char>(chars, 0, charsWritten);
@@ -549,7 +549,7 @@ public override int ToUtf8(ReadOnlySpan<char> source, Span<byte> destination)
549549

550550
public override int FromUtf8(ReadOnlySpan<byte> source, Span<char> destination)
551551
{
552-
var bytes = source.ToArray();
552+
var bytes = source.WithoutBom().ToArray();
553553
var chars = destination.ToArray();
554554
var charsWritten = Encoding.UTF8.GetChars(bytes, 0, source.Length, chars, 0);
555555
new ReadOnlySpan<char>(chars, 0, charsWritten).CopyTo(destination);
@@ -558,7 +558,7 @@ public override int FromUtf8(ReadOnlySpan<byte> source, Span<char> destination)
558558

559559
public override byte[] ToUtf8Bytes(ReadOnlySpan<char> source) => Encoding.UTF8.GetBytes(source.ToArray());
560560

561-
public override string FromUtf8Bytes(ReadOnlySpan<byte> source) => Encoding.UTF8.GetString(source.ToArray());
561+
public override string FromUtf8Bytes(ReadOnlySpan<byte> source) => Encoding.UTF8.GetString(source.WithoutBom().ToArray());
562562

563563
public override MemoryStream ToMemoryStream(ReadOnlySpan<byte> source) =>
564564
MemoryStreamFactory.GetStream(source.ToArray());

src/ServiceStack.Text/Json/JsonReader.Generic.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ public static object Parse(ReadOnlySpan<char> value)
8888
{
8989
TypeConfig<T>.Init();
9090

91+
value = value.WithoutBom();
92+
9193
if (ReadFn == null)
9294
{
9395
if (typeof(T).IsAbstract || typeof(T).IsInterface)

src/ServiceStack.Text/Jsv/JsvReader.Generic.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ public static object Parse(ReadOnlySpan<char> value)
8585
{
8686
TypeConfig<T>.Init();
8787

88+
value = value.WithoutBom();
89+
8890
if (ReadFn == null)
8991
{
9092
if (typeof(T).IsInterface)

src/ServiceStack.Text/NetCoreMemory.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,18 +177,19 @@ public override ReadOnlyMemory<byte> ToUtf8(ReadOnlySpan<char> source)
177177

178178
public override ReadOnlyMemory<char> FromUtf8(ReadOnlySpan<byte> source)
179179
{
180+
source = source.WithoutBom();
180181
Memory<char> chars = new char[Encoding.UTF8.GetCharCount(source)];
181182
var charsWritten = Encoding.UTF8.GetChars(source, chars.Span);
182183
return chars.Slice(0, charsWritten);
183184
}
184185

185186
public override int ToUtf8(ReadOnlySpan<char> source, Span<byte> destination) => Encoding.UTF8.GetBytes(source, destination);
186187

187-
public override int FromUtf8(ReadOnlySpan<byte> source, Span<char> destination) => Encoding.UTF8.GetChars(source, destination);
188+
public override int FromUtf8(ReadOnlySpan<byte> source, Span<char> destination) => Encoding.UTF8.GetChars(source.WithoutBom(), destination);
188189

189190
public override byte[] ToUtf8Bytes(ReadOnlySpan<char> source) => ToUtf8(source).ToArray();
190191

191-
public override string FromUtf8Bytes(ReadOnlySpan<byte> source) => FromUtf8(source).ToString();
192+
public override string FromUtf8Bytes(ReadOnlySpan<byte> source) => FromUtf8(source.WithoutBom()).ToString();
192193

193194
public override MemoryStream ToMemoryStream(ReadOnlySpan<byte> source)
194195
{

src/ServiceStack.Text/StringExtensions.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,9 @@ public static string AppendUrlPathsRaw(this string uri, params string[] uriCompo
270270
public static string FromUtf8Bytes(this byte[] bytes)
271271
{
272272
return bytes == null ? null
273-
: Encoding.UTF8.GetString(bytes, 0, bytes.Length);
273+
: bytes.Length > 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF
274+
? Encoding.UTF8.GetString(bytes, 3, bytes.Length - 3)
275+
: Encoding.UTF8.GetString(bytes, 0, bytes.Length);
274276
}
275277

276278
public static byte[] ToUtf8Bytes(this string value)

src/ServiceStack.Text/StringSpanExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,5 +661,20 @@ public static int CountOccurrencesOf(this ReadOnlySpan<char> value, char needle)
661661
}
662662
return count;
663663
}
664+
665+
public static ReadOnlySpan<char> WithoutBom(this ReadOnlySpan<char> value)
666+
{
667+
return value.Length > 0 && value[0] == 65279
668+
? value.Slice(1)
669+
: value;
670+
}
671+
672+
public static ReadOnlySpan<byte> WithoutBom(this ReadOnlySpan<byte> value)
673+
{
674+
return value.Length > 3 && value[0] == 0xEF && value[1] == 0xBB && value[2] == 0xBF
675+
? value.Slice(3)
676+
: value;
677+
}
678+
664679
}
665680
}

tests/ServiceStack.Text.Tests/SpanTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
34
using System.Threading.Tasks;
45
using NUnit.Framework;
56
using ServiceStack.Text.Pools;
@@ -149,6 +150,25 @@ public async Task Can_deserialize_from_MemoryStream_using_Memory()
149150

150151
Assert.That(to, Is.EqualTo(from));
151152
}
153+
154+
[Test]
155+
public void Can_deserialize_JSON_with_UTF8_BOM()
156+
{
157+
var from = new Person { Id = 1, Name = "Foo" };
158+
var json = from.ToJson();
159+
var jsonBytes = json.ToUtf8Bytes();
160+
161+
var bytes = new List<byte>(new byte[] { 0xEF, 0xBB, 0xBF });
162+
bytes.AddRange(jsonBytes);
163+
164+
var mergedBytes = bytes.ToArray();
165+
166+
var jsonWithBOM = mergedBytes.FromUtf8Bytes();
167+
168+
var fromJsonWithBOM = jsonWithBOM.FromJson<Person>();
169+
170+
Assert.That(fromJsonWithBOM, Is.EqualTo(from));
171+
}
152172
}
153173

154174
public class Utf8Case

0 commit comments

Comments
 (0)