private static ReadOnlySpan<int> DaysToMonth366 => new int[] {
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};
+ private const int CharStackBufferSize = 64;
+
/// <summary>
/// Constructs an XsdDateTime from a string using specific format.
/// </summary>
/// </summary>
public override string ToString()
{
- var vsb = new ValueStringBuilder(stackalloc char[64]);
+ Span<char> destination = stackalloc char[CharStackBufferSize];
+ bool success = TryFormat(destination, out int charsWritten);
+ Debug.Assert(success);
+
+ return destination.Slice(0, charsWritten).ToString();
+ }
+
+ public bool TryFormat(Span<char> destination, out int charsWritten)
+ {
+ var vsb = new ValueStringBuilder(destination);
+
switch (InternalTypeCode)
{
case DateTimeTypeCode.DateTime:
break;
}
PrintZone(ref vsb);
- return vsb.ToString();
+
+ charsWritten = vsb.Length;
+ return destination.Length >= vsb.Length;
}
// Serialize year, month and day
private uint _nanoseconds; // High bit is used to indicate whether duration is negative
private const uint NegativeBit = 0x80000000;
+ private const int CharStackBufferSize = 32;
private enum Parts
{
/// </summary>
internal string ToString(DurationType durationType)
{
- var vsb = new ValueStringBuilder(stackalloc char[20]);
+ Span<char> destination = stackalloc char[CharStackBufferSize];
+ bool success = TryFormat(destination, out int charsWritten, durationType);
+ Debug.Assert(success);
+
+ return destination.Slice(0, charsWritten).ToString();
+ }
+
+ public bool TryFormat(Span<char> destination, out int charsWritten, DurationType durationType = DurationType.Duration)
+ {
+ var vsb = new ValueStringBuilder(destination);
int nanoseconds, digit, zeroIdx, len;
if (IsNegative)
}
vsb.EnsureCapacity(zeroIdx + 1);
- vsb.Append(tmpSpan.Slice(0, zeroIdx - len + 1));
+ int nanoSpanLength = zeroIdx - len + 1;
+ bool successCopy = tmpSpan[..nanoSpanLength].TryCopyTo(vsb.AppendSpan(nanoSpanLength));
+ Debug.Assert(successCopy);
}
vsb.Append('S');
}
vsb.Append("0M");
}
- return vsb.ToString();
+ charsWritten = vsb.Length;
+ return destination.Length >= vsb.Length;
}
internal static Exception? TryParse(string s, out XsdDuration result)
private bool _soap12;
private bool _escapeName = true;
+ //char buffer for serializing primitive values
+ private readonly char[] _primitivesBuffer = new char[64];
+
// this method must be called before any generated serialization methods are called
internal void Init(XmlWriter w, XmlSerializerNamespaces? namespaces, string? encodingStyle, string? idBase)
{
return XmlCustomFormatter.FromDateTime(value);
}
+ internal static bool TryFormatDateTime(DateTime value, Span<char> destination, out int charsWritten)
+ {
+ return XmlCustomFormatter.TryFormatDateTime(value, destination, out charsWritten);
+ }
+
protected static string FromDate(DateTime value)
{
return XmlCustomFormatter.FromDate(value);
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected void WriteTypedPrimitive(string? name, string? ns, object o, bool xsiType)
{
- string? value;
+ string? value = null;
string type;
string typeNs = XmlSchema.Namespace;
bool writeRaw = true;
bool writeDirect = false;
Type t = o.GetType();
bool wroteStartElement = false;
+ bool? tryFormatResult = null;
+ int charsWritten = -1;
switch (Type.GetTypeCode(t))
{
writeRaw = false;
break;
case TypeCode.Int32:
- value = XmlConvert.ToString((int)o);
+ tryFormatResult = XmlConvert.TryFormat((int)o, _primitivesBuffer, out charsWritten);
type = "int";
break;
case TypeCode.Boolean:
- value = XmlConvert.ToString((bool)o);
+ tryFormatResult = XmlConvert.TryFormat((bool)o, _primitivesBuffer, out charsWritten);
type = "boolean";
break;
case TypeCode.Int16:
- value = XmlConvert.ToString((short)o);
+ tryFormatResult = XmlConvert.TryFormat((short)o, _primitivesBuffer, out charsWritten);
type = "short";
break;
case TypeCode.Int64:
- value = XmlConvert.ToString((long)o);
+ tryFormatResult = XmlConvert.TryFormat((long)o, _primitivesBuffer, out charsWritten);
type = "long";
break;
case TypeCode.Single:
- value = XmlConvert.ToString((float)o);
+ tryFormatResult = XmlConvert.TryFormat((float)o, _primitivesBuffer, out charsWritten);
type = "float";
break;
case TypeCode.Double:
- value = XmlConvert.ToString((double)o);
+ tryFormatResult = XmlConvert.TryFormat((double)o, _primitivesBuffer, out charsWritten);
type = "double";
break;
case TypeCode.Decimal:
- value = XmlConvert.ToString((decimal)o);
+ tryFormatResult = XmlConvert.TryFormat((decimal)o, _primitivesBuffer, out charsWritten);
type = "decimal";
break;
case TypeCode.DateTime:
- value = FromDateTime((DateTime)o);
+ tryFormatResult = TryFormatDateTime((DateTime)o, _primitivesBuffer, out charsWritten);
type = "dateTime";
break;
case TypeCode.Char:
- value = FromChar((char)o);
+ tryFormatResult = XmlConvert.TryFormat((ushort)(char)o, _primitivesBuffer, out charsWritten);
type = "char";
typeNs = UrtTypes.Namespace;
break;
case TypeCode.Byte:
- value = XmlConvert.ToString((byte)o);
+ tryFormatResult = XmlConvert.TryFormat((byte)o, _primitivesBuffer, out charsWritten);
type = "unsignedByte";
break;
case TypeCode.SByte:
- value = XmlConvert.ToString((sbyte)o);
+ tryFormatResult = XmlConvert.TryFormat((sbyte)o, _primitivesBuffer, out charsWritten);
type = "byte";
break;
case TypeCode.UInt16:
- value = XmlConvert.ToString((ushort)o);
+ tryFormatResult = XmlConvert.TryFormat((ushort)o, _primitivesBuffer, out charsWritten);
type = "unsignedShort";
break;
case TypeCode.UInt32:
- value = XmlConvert.ToString((uint)o);
+ tryFormatResult = XmlConvert.TryFormat((uint)o, _primitivesBuffer, out charsWritten);
type = "unsignedInt";
break;
case TypeCode.UInt64:
- value = XmlConvert.ToString((ulong)o);
+ tryFormatResult = XmlConvert.TryFormat((ulong)o, _primitivesBuffer, out charsWritten);
type = "unsignedLong";
break;
}
else if (t == typeof(Guid))
{
- value = XmlConvert.ToString((Guid)o);
+ tryFormatResult = XmlConvert.TryFormat((Guid)o, _primitivesBuffer, out charsWritten);
type = "guid";
typeNs = UrtTypes.Namespace;
}
else if (t == typeof(TimeSpan))
{
- value = XmlConvert.ToString((TimeSpan)o);
+ tryFormatResult = XmlConvert.TryFormat((TimeSpan)o, _primitivesBuffer, out charsWritten);
type = "TimeSpan";
typeNs = UrtTypes.Namespace;
}
else if (t == typeof(DateTimeOffset))
{
- value = XmlConvert.ToString((DateTimeOffset)o);
+ tryFormatResult = XmlConvert.TryFormat((DateTimeOffset)o, _primitivesBuffer, out charsWritten);
type = "dateTimeOffset";
typeNs = UrtTypes.Namespace;
}
return;
}
else
+ {
throw CreateUnknownTypeException(t);
+ }
+
break;
}
if (!wroteStartElement)
if (xsiType) WriteXsiType(type, typeNs);
- if (value == null)
- {
- _w.WriteAttributeString("nil", XmlSchema.InstanceNamespace, "true");
- }
- else if (writeDirect)
+ if (writeDirect)
{
// only one type currently writes directly to XML stream
XmlCustomFormatter.WriteArrayBase64(_w, (byte[])o, 0, ((byte[])o).Length);
}
- else if (writeRaw)
+ else if (tryFormatResult != null)
{
- _w.WriteRaw(value);
+ Debug.Assert(tryFormatResult.Value, "Something goes wrong with formatting primitives to the buffer.");
+#if DEBUG
+ const string escapeChars = "<>\"'&";
+ ReadOnlySpan<char> span = _primitivesBuffer;
+ Debug.Assert(span.Slice(0, charsWritten).IndexOfAny(escapeChars) == -1, "Primitive value contains illegal xml char.");
+#endif
+ //all the primitive types except string and XmlQualifiedName writes to the buffer
+ _w.WriteRaw(_primitivesBuffer, 0, charsWritten);
}
else
- _w.WriteString(value);
+ {
+ if (value == null)
+ _w.WriteAttributeString("nil", XmlSchema.InstanceNamespace, "true");
+ else if (writeRaw)
+ {
+ _w.WriteRaw(value);
+ }
+ else
+ _w.WriteString(value);
+ }
+
_w.WriteEndElement();
}
}
}
+ internal static bool TryFormatDateTime(DateTime value, Span<char> destination, out int charsWritten)
+ {
+ if (Mode == DateTimeSerializationSection.DateTimeSerializationMode.Local)
+ {
+ return XmlConvert.TryFormat(value, "yyyy-MM-ddTHH:mm:ss.fffffffzzzzzz", destination, out charsWritten);
+ }
+
+ // for mode DateTimeSerializationMode.Roundtrip and DateTimeSerializationMode.Default
+ return XmlConvert.TryFormat(value, XmlDateTimeSerializationMode.RoundtripKind, destination, out charsWritten);
+ }
+
internal static string FromChar(char value)
{
return XmlConvert.ToString((ushort)value);
{
return CreateException(index == 0 ? SR.Xml_BadStartNameChar : SR.Xml_BadNameChar, XmlException.BuildCharExceptionArgs(name, index), exceptionType, 0, index + 1);
}
+
+ internal static bool TryFormat(bool value, Span<char> destination, out int charsWritten)
+ {
+ string valueAsString = value ? "true" : "false";
+
+ charsWritten = valueAsString.Length;
+ return valueAsString.TryCopyTo(destination);
+ }
+
+ internal static bool TryFormat(char value, Span<char> destination, out int charsWritten)
+ {
+ charsWritten = 1;
+ if (destination.Length < 1) return false;
+
+ destination[0] = value;
+ return true;
+ }
+
+ internal static bool TryFormat(decimal value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, default, NumberFormatInfo.InvariantInfo);
+ }
+
+ internal static bool TryFormat(sbyte value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, default, CultureInfo.InvariantCulture);
+ }
+
+ internal static bool TryFormat(short value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, default, CultureInfo.InvariantCulture);
+ }
+
+ internal static bool TryFormat(int value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, default, CultureInfo.InvariantCulture);
+ }
+
+ internal static bool TryFormat(long value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, default, CultureInfo.InvariantCulture);
+ }
+
+ internal static bool TryFormat(byte value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, default, CultureInfo.InvariantCulture);
+ }
+
+ internal static bool TryFormat(ushort value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, default, CultureInfo.InvariantCulture);
+ }
+
+ internal static bool TryFormat(uint value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, default, CultureInfo.InvariantCulture);
+ }
+
+ internal static bool TryFormat(ulong value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, default, CultureInfo.InvariantCulture);
+ }
+
+ internal static bool TryFormat(float value, Span<char> destination, out int charsWritten)
+ {
+ ReadOnlySpan<char> valueSpan;
+
+ if (!float.IsFinite(value))
+ {
+ if (float.IsNaN(value))
+ valueSpan = "NaN";
+ else
+ valueSpan = float.IsNegative(value) ? "-INF" : "INF";
+ }
+ else if (IsNegativeZero((double)value))
+ {
+ valueSpan = "-0";
+ }
+ else
+ {
+ return value.TryFormat(destination, out charsWritten, "R", NumberFormatInfo.InvariantInfo);
+ }
+
+ charsWritten = valueSpan.Length;
+ return valueSpan.TryCopyTo(destination);
+ }
+
+ internal static bool TryFormat(double value, Span<char> destination, out int charsWritten)
+ {
+ ReadOnlySpan<char> valueSpan;
+
+ if (!double.IsFinite(value))
+ {
+ if (double.IsNaN(value))
+ valueSpan = "NaN";
+ else
+ valueSpan = double.IsNegative(value) ? "-INF" : "INF";
+ }
+ else if (IsNegativeZero(value))
+ {
+ valueSpan = "-0";
+ }
+ else
+ {
+ return value.TryFormat(destination, out charsWritten, "R", NumberFormatInfo.InvariantInfo);
+ }
+
+ charsWritten = valueSpan.Length;
+ return valueSpan.TryCopyTo(destination);
+ }
+
+ internal static bool TryFormat(TimeSpan value, Span<char> destination, out int charsWritten)
+ {
+ return new XsdDuration(value).TryFormat(destination, out charsWritten);
+ }
+
+ internal static bool TryFormat(DateTime value, [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] string format, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, format, DateTimeFormatInfo.InvariantInfo);
+ }
+
+ internal static bool TryFormat(DateTime value, Span<char> destination, out int charsWritten)
+ {
+ return TryFormat(value, XmlDateTimeSerializationMode.RoundtripKind, destination, out charsWritten);
+ }
+
+ internal static bool TryFormat(DateTime value, XmlDateTimeSerializationMode dateTimeOption, Span<char> destination, out int charsWritten)
+ {
+ switch (dateTimeOption)
+ {
+ case XmlDateTimeSerializationMode.Local:
+ value = SwitchToLocalTime(value);
+ break;
+
+ case XmlDateTimeSerializationMode.Utc:
+ value = SwitchToUtcTime(value);
+ break;
+
+ case XmlDateTimeSerializationMode.Unspecified:
+ value = new DateTime(value.Ticks, DateTimeKind.Unspecified);
+ break;
+
+ case XmlDateTimeSerializationMode.RoundtripKind:
+ break;
+
+ default:
+ throw new ArgumentException(SR.Format(SR.Sch_InvalidDateTimeOption, dateTimeOption, nameof(dateTimeOption)));
+ }
+
+ XsdDateTime xsdDateTime = new XsdDateTime(value, XsdDateTimeFlags.DateTime);
+ return xsdDateTime.TryFormat(destination, out charsWritten);
+ }
+
+ internal static bool TryFormat(DateTimeOffset value, Span<char> destination, out int charsWritten)
+ {
+ XsdDateTime xsdDateTime = new XsdDateTime(value);
+ return xsdDateTime.TryFormat(destination, out charsWritten);
+ }
+
+ internal static bool TryFormat(DateTimeOffset value, [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] string format, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten, format, DateTimeFormatInfo.InvariantInfo);
+ }
+
+ internal static bool TryFormat(Guid value, Span<char> destination, out int charsWritten)
+ {
+ return value.TryFormat(destination, out charsWritten);
+ }
}
}
}
[Fact]
+ public static void Xml_CollectionRoot_MorePrimitiveTypes()
+ {
+ DateTime now = new DateTime(2022, 9, 30, 9, 4, 15, DateTimeKind.Utc);
+ DateTimeOffset dtoNow = now.AddDays(1);
+ TimeSpan ts = new TimeSpan(1, 2, 3, 4, 5);
+ MyCollection x = new MyCollection(123.45m, now, ts, dtoNow, (short)55, 2345324L, (sbyte)11, (ushort)34, (uint)4564, (ulong)456734767,
+ new byte[] { 33, 44, 55 }, (byte)67);
+ MyCollection y = SerializeAndDeserialize<MyCollection>(x,
+@"<?xml version=""1.0""?>
+<ArrayOfAnyType xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
+ <anyType xsi:type=""xsd:decimal"">123.45</anyType>
+ <anyType xsi:type=""xsd:dateTime"">2022-09-30T09:04:15Z</anyType>
+ <anyType xmlns:q2=""http://microsoft.com/wsdl/types/"" xsi:type=""q2:TimeSpan"">P1DT2H3M4.005S</anyType>
+ <anyType xmlns:q3=""http://microsoft.com/wsdl/types/"" xsi:type=""q3:dateTimeOffset"">2022-10-01T09:04:15Z</anyType>
+ <anyType xsi:type=""xsd:short"">55</anyType>
+ <anyType xsi:type=""xsd:long"">2345324</anyType>
+ <anyType xsi:type=""xsd:byte"">11</anyType>
+ <anyType xsi:type=""xsd:unsignedShort"">34</anyType>
+ <anyType xsi:type=""xsd:unsignedInt"">4564</anyType>
+ <anyType xsi:type=""xsd:unsignedLong"">456734767</anyType>
+ <anyType xsi:type=""xsd:base64Binary"">ISw3</anyType>
+ <anyType xsi:type=""xsd:unsignedByte"">67</anyType>
+</ArrayOfAnyType>");
+
+ Assert.NotNull(y);
+ Assert.True(y.Count == 12);
+ Assert.True((decimal)y[0] == 123.45m);
+ Assert.True((DateTime)y[1] == now);
+ Assert.True((TimeSpan)y[2] == ts);
+ Assert.True((DateTimeOffset)y[3] == dtoNow);
+ Assert.True((short)y[4] == 55);
+ Assert.True((long)y[5] == 2345324L);
+ Assert.True((sbyte)y[6] == 11);
+ Assert.True((ushort)y[7] == 34);
+ Assert.True((uint)y[8] == 4564);
+ Assert.True((ulong)y[9] == 456734767);
+ Assert.True(y[10] is byte[]);
+ Assert.Equal(3, ((byte[])y[10]).Length);
+ Assert.Equal(33, ((byte[])y[10])[0]);
+ Assert.Equal(44, ((byte[])y[10])[1]);
+ Assert.Equal(55, ((byte[])y[10])[2]);
+ Assert.True((byte)y[11] == 67);
+ }
+
+ [Fact]
public static void Xml_EnumerableRoot()
{
MyEnumerable x = new MyEnumerable("abc", 3);