@@ -19,6 +19,7 @@ public static class TomlSerializer
1919
2020 private static readonly bool ReflectionEnabledByDefault = TomlSerializerFeatureSwitches . IsReflectionEnabledByDefaultCalculated ;
2121 private static readonly Encoding DefaultStreamEncoding = new UTF8Encoding ( encoderShouldEmitUTF8Identifier : false , throwOnInvalidBytes : true ) ;
22+ private static ReadOnlySpan < byte > Utf8Bom => [ 0xEF , 0xBB , 0xBF ] ;
2223
2324 /// <summary>
2425 /// Gets a value indicating whether reflection-based serialization is enabled by default.
@@ -246,8 +247,11 @@ public static void Serialize(Stream utf8Stream, object? value, Type inputType, T
246247 public static T ? Deserialize < T > ( Stream utf8Stream , TomlSerializerOptions ? options = null )
247248 {
248249 ArgumentGuard . ThrowIfNull ( utf8Stream , nameof ( utf8Stream ) ) ;
249- using var reader = new StreamReader ( utf8Stream , DefaultStreamEncoding , detectEncodingFromByteOrderMarks : true , bufferSize : 1024 , leaveOpen : true ) ;
250- return Deserialize < T > ( reader , options ) ;
250+
251+ var utf8Bytes = ReadUtf8Bytes ( utf8Stream ) ;
252+ var effectiveOptions = options ?? TomlSerializerOptions . Default ;
253+ var typeInfo = ResolveTypeInfo ( effectiveOptions , typeof ( T ) ) ;
254+ return ( T ? ) Deserialize ( utf8Bytes , typeInfo ) ;
251255 }
252256
253257 /// <summary>
@@ -257,8 +261,8 @@ public static void Serialize(Stream utf8Stream, object? value, Type inputType, T
257261 {
258262 ArgumentGuard . ThrowIfNull ( utf8Stream , nameof ( utf8Stream ) ) ;
259263 ArgumentGuard . ThrowIfNull ( typeInfo , nameof ( typeInfo ) ) ;
260- using var reader = new StreamReader ( utf8Stream , DefaultStreamEncoding , detectEncodingFromByteOrderMarks : true , bufferSize : 1024 , leaveOpen : true ) ;
261- return Deserialize < T > ( reader , typeInfo ) ;
264+ var utf8Bytes = ReadUtf8Bytes ( utf8Stream ) ;
265+ return ( T ? ) Deserialize ( utf8Bytes , ( TomlTypeInfo ) typeInfo ) ;
262266 }
263267
264268 /// <summary>
@@ -270,8 +274,11 @@ public static void Serialize(Stream utf8Stream, object? value, Type inputType, T
270274 {
271275 ArgumentGuard . ThrowIfNull ( utf8Stream , nameof ( utf8Stream ) ) ;
272276 ArgumentGuard . ThrowIfNull ( returnType , nameof ( returnType ) ) ;
273- using var reader = new StreamReader ( utf8Stream , DefaultStreamEncoding , detectEncodingFromByteOrderMarks : true , bufferSize : 1024 , leaveOpen : true ) ;
274- return Deserialize ( reader , returnType , options ) ;
277+
278+ var utf8Bytes = ReadUtf8Bytes ( utf8Stream ) ;
279+ var effectiveOptions = options ?? TomlSerializerOptions . Default ;
280+ var typeInfo = ResolveTypeInfo ( effectiveOptions , returnType ) ;
281+ return Deserialize ( utf8Bytes , typeInfo ) ;
275282 }
276283
277284 /// <summary>
@@ -325,6 +332,21 @@ public static bool TryDeserialize(string toml, Type returnType, out object? valu
325332
326333 var options = typeInfo . Options ;
327334 var reader = TomlReader . Create ( toml , options ) ;
335+ return DeserializeCore ( reader , typeInfo , options ) ;
336+ }
337+
338+ private static object ? Deserialize ( byte [ ] utf8Toml , TomlTypeInfo typeInfo )
339+ {
340+ ArgumentGuard . ThrowIfNull ( utf8Toml , nameof ( utf8Toml ) ) ;
341+ ArgumentGuard . ThrowIfNull ( typeInfo , nameof ( typeInfo ) ) ;
342+
343+ var options = typeInfo . Options ;
344+ var reader = TomlReader . Create ( utf8Toml , options ) ;
345+ return DeserializeCore ( reader , typeInfo , options ) ;
346+ }
347+
348+ private static object ? DeserializeCore ( TomlReader reader , TomlTypeInfo typeInfo , TomlSerializerOptions options )
349+ {
328350 reader . Read ( ) ; // StartDocument
329351 reader . Read ( ) ; // value start
330352
@@ -359,6 +381,34 @@ public static bool TryDeserialize(string toml, Type returnType, out object? valu
359381 return typeInfo . ReadAsObject ( reader ) ;
360382 }
361383
384+ private static byte [ ] ReadUtf8Bytes ( Stream utf8Stream )
385+ {
386+ if ( utf8Stream is MemoryStream memoryStream && memoryStream . TryGetBuffer ( out var buffer ) &&
387+ memoryStream . Position <= int . MaxValue && memoryStream . Length - memoryStream . Position <= int . MaxValue )
388+ {
389+ var start = buffer . Offset + ( int ) memoryStream . Position ;
390+ var length = ( int ) ( memoryStream . Length - memoryStream . Position ) ;
391+ var data = new ReadOnlySpan < byte > ( buffer . Array ! , start , length ) ;
392+ return StripUtf8Bom ( data ) . ToArray ( ) ;
393+ }
394+
395+ using var copy = new MemoryStream ( ) ;
396+ utf8Stream . CopyTo ( copy ) ;
397+ var bytes = copy . ToArray ( ) ;
398+ var stripped = StripUtf8Bom ( bytes ) ;
399+ return stripped . Length == bytes . Length ? bytes : stripped . ToArray ( ) ;
400+ }
401+
402+ private static ReadOnlySpan < byte > StripUtf8Bom ( ReadOnlySpan < byte > data )
403+ {
404+ if ( data . Length >= 3 && data [ 0 ] == Utf8Bom [ 0 ] && data [ 1 ] == Utf8Bom [ 1 ] && data [ 2 ] == Utf8Bom [ 2 ] )
405+ {
406+ return data . Slice ( 3 ) ;
407+ }
408+
409+ return data ;
410+ }
411+
362412 /// <summary>
363413 /// Serializes a value to a writer using explicit metadata.
364414 /// </summary>
0 commit comments