diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj
index d091b9072d..1cb29e4e37 100644
--- a/src/Avalonia.Base/Avalonia.Base.csproj
+++ b/src/Avalonia.Base/Avalonia.Base.csproj
@@ -35,8 +35,10 @@
+
+
diff --git a/src/Avalonia.Base/Input/KeyGesture.cs b/src/Avalonia.Base/Input/KeyGesture.cs
index e79e9341a9..1a6372d346 100644
--- a/src/Avalonia.Base/Input/KeyGesture.cs
+++ b/src/Avalonia.Base/Input/KeyGesture.cs
@@ -96,7 +96,7 @@ namespace Avalonia.Input
public override string ToString()
{
- var s = new StringBuilder();
+ var s = StringBuilderCache.Acquire();
static void Plus(StringBuilder s)
{
@@ -132,7 +132,7 @@ namespace Avalonia.Input
Plus(s);
s.Append(Key);
- return s.ToString();
+ return StringBuilderCache.GetStringAndRelease(s);
}
public bool Matches(KeyEventArgs keyEvent) =>
diff --git a/src/Avalonia.Base/Logging/TraceLogSink.cs b/src/Avalonia.Base/Logging/TraceLogSink.cs
index 05e4b8bc5a..fc3897fade 100644
--- a/src/Avalonia.Base/Logging/TraceLogSink.cs
+++ b/src/Avalonia.Base/Logging/TraceLogSink.cs
@@ -46,7 +46,7 @@ namespace Avalonia.Logging
object? source,
object?[]? values)
{
- var result = new StringBuilder(template.Length);
+ var result = StringBuilderCache.Acquire(template.Length);
var r = new CharacterReader(template.AsSpan());
var i = 0;
@@ -89,7 +89,7 @@ namespace Avalonia.Logging
result.Append(')');
}
- return result.ToString();
+ return StringBuilderCache.GetStringAndRelease(result);
}
private static string Format(
@@ -98,7 +98,7 @@ namespace Avalonia.Logging
object? source,
object?[] v)
{
- var result = new StringBuilder(template.Length);
+ var result = StringBuilderCache.Acquire(template.Length);
var r = new CharacterReader(template.AsSpan());
var i = 0;
diff --git a/src/Avalonia.Base/Media/BoxShadow.cs b/src/Avalonia.Base/Media/BoxShadow.cs
index b01f59f5f8..cc97d89cfc 100644
--- a/src/Avalonia.Base/Media/BoxShadow.cs
+++ b/src/Avalonia.Base/Media/BoxShadow.cs
@@ -80,7 +80,7 @@ namespace Avalonia.Media
public override string ToString()
{
- var sb = new StringBuilder();
+ var sb = StringBuilderCache.Acquire();
if (IsEmpty)
{
@@ -114,7 +114,7 @@ namespace Avalonia.Media
sb.AppendFormat(" {0}", Color.ToString());
- return sb.ToString();
+ return StringBuilderCache.GetStringAndRelease(sb);
}
public static unsafe BoxShadow Parse(string s)
diff --git a/src/Avalonia.Base/Media/BoxShadows.cs b/src/Avalonia.Base/Media/BoxShadows.cs
index 4614ea4e3c..ab2694389f 100644
--- a/src/Avalonia.Base/Media/BoxShadows.cs
+++ b/src/Avalonia.Base/Media/BoxShadows.cs
@@ -1,7 +1,7 @@
using System;
using System.ComponentModel;
-using System.Text;
using Avalonia.Animation.Animators;
+using Avalonia.Utilities;
namespace Avalonia.Media
{
@@ -45,7 +45,7 @@ namespace Avalonia.Media
public override string ToString()
{
- var sb = new StringBuilder();
+ var sb = StringBuilderCache.Acquire();
if (Count == 0)
{
@@ -57,7 +57,7 @@ namespace Avalonia.Media
sb.AppendFormat("{0} ", boxShadow.ToString());
}
- return sb.ToString();
+ return StringBuilderCache.GetStringAndRelease(sb);
}
diff --git a/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs b/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs
index 99daaf2143..eb42f6443b 100644
--- a/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs
+++ b/src/Avalonia.Base/Media/Fonts/FamilyNameCollection.cs
@@ -77,7 +77,7 @@ namespace Avalonia.Media.Fonts
///
public override string ToString()
{
- var builder = new StringBuilder();
+ var builder = StringBuilderCache.Acquire();
for (var index = 0; index < Names.Count; index++)
{
@@ -91,7 +91,7 @@ namespace Avalonia.Media.Fonts
builder.Append(", ");
}
- return builder.ToString();
+ return StringBuilderCache.GetStringAndRelease(builder);
}
///
diff --git a/src/Avalonia.Base/Media/HslColor.cs b/src/Avalonia.Base/Media/HslColor.cs
index e8a4d6f94f..485bb1db16 100644
--- a/src/Avalonia.Base/Media/HslColor.cs
+++ b/src/Avalonia.Base/Media/HslColor.cs
@@ -202,7 +202,7 @@ namespace Avalonia.Media
///
public override string ToString()
{
- var sb = new StringBuilder();
+ var sb = StringBuilderCache.Acquire();
// Use a format similar to CSS. However:
// - To ensure precision is never lost, allow decimal places.
@@ -225,7 +225,7 @@ namespace Avalonia.Media
sb.Append(A.ToString(CultureInfo.InvariantCulture));
sb.Append(')');
- return sb.ToString();
+ return StringBuilderCache.GetStringAndRelease(sb);
}
///
diff --git a/src/Avalonia.Base/Media/HsvColor.cs b/src/Avalonia.Base/Media/HsvColor.cs
index 924ef4778b..512e57ae07 100644
--- a/src/Avalonia.Base/Media/HsvColor.cs
+++ b/src/Avalonia.Base/Media/HsvColor.cs
@@ -202,7 +202,7 @@ namespace Avalonia.Media
///
public override string ToString()
{
- var sb = new StringBuilder();
+ var sb = StringBuilderCache.Acquire();
// Use a format similar to CSS. However:
// - To ensure precision is never lost, allow decimal places.
@@ -225,7 +225,7 @@ namespace Avalonia.Media
sb.Append(A.ToString(CultureInfo.InvariantCulture));
sb.Append(')');
- return sb.ToString();
+ return StringBuilderCache.GetStringAndRelease(sb);
}
///
diff --git a/src/Avalonia.Base/Styling/NthChildSelector.cs b/src/Avalonia.Base/Styling/NthChildSelector.cs
index f473791664..c872a40ad4 100644
--- a/src/Avalonia.Base/Styling/NthChildSelector.cs
+++ b/src/Avalonia.Base/Styling/NthChildSelector.cs
@@ -1,8 +1,8 @@
#nullable enable
using System;
-using System.Text;
using Avalonia.LogicalTree;
using Avalonia.Styling.Activators;
+using Avalonia.Utilities;
namespace Avalonia.Styling
{
@@ -110,7 +110,8 @@ namespace Avalonia.Styling
public override string ToString()
{
var expectedCapacity = NthLastChildSelectorName.Length + 8;
- var stringBuilder = new StringBuilder(_previous?.ToString(), expectedCapacity);
+ var stringBuilder = StringBuilderCache.Acquire(expectedCapacity);
+ stringBuilder.Append(_previous?.ToString());
stringBuilder.Append(':');
stringBuilder.Append(_reversed ? NthLastChildSelectorName : NthChildSelectorName);
@@ -140,7 +141,7 @@ namespace Avalonia.Styling
stringBuilder.Append(')');
- return stringBuilder.ToString();
+ return StringBuilderCache.GetStringAndRelease(stringBuilder);
}
}
}
diff --git a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs
index 48136ba2de..e98ff3f9c9 100644
--- a/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs
+++ b/src/Avalonia.Base/Styling/PropertyEqualsSelector.cs
@@ -1,8 +1,8 @@
using System;
using System.ComponentModel;
using System.Globalization;
-using System.Text;
using Avalonia.Styling.Activators;
+using Avalonia.Utilities;
#nullable enable
@@ -42,7 +42,7 @@ namespace Avalonia.Styling
{
if (_selectorString == null)
{
- var builder = new StringBuilder();
+ var builder = StringBuilderCache.Acquire();
if (_previous != null)
{
@@ -67,7 +67,7 @@ namespace Avalonia.Styling
builder.Append(_value ?? string.Empty);
builder.Append(']');
- _selectorString = builder.ToString();
+ _selectorString = StringBuilderCache.GetStringAndRelease(builder);
}
return _selectorString;
diff --git a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
index 94a6db41f6..1833f0d133 100644
--- a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
+++ b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Text;
using Avalonia.Controls;
using Avalonia.Styling.Activators;
+using Avalonia.Utilities;
#nullable enable
@@ -145,7 +145,7 @@ namespace Avalonia.Styling
private string BuildSelectorString()
{
- var builder = new StringBuilder();
+ var builder = StringBuilderCache.Acquire();
if (_previous != null)
{
@@ -185,7 +185,7 @@ namespace Avalonia.Styling
}
}
- return builder.ToString();
+ return StringBuilderCache.GetStringAndRelease(builder);
}
}
}
diff --git a/src/Avalonia.Base/Utilities/StringBuilderCache.cs b/src/Avalonia.Base/Utilities/StringBuilderCache.cs
new file mode 100644
index 0000000000..be8b24c848
--- /dev/null
+++ b/src/Avalonia.Base/Utilities/StringBuilderCache.cs
@@ -0,0 +1,68 @@
+// This file is imported from dotnet/runtime
+// Source Link: https://github.com/dotnet/runtime/blob/e63d21947e734db2da5093510a6636b5b7fb45b5/src/libraries/Common/src/System/Text/StringBuilderCache.cs
+// Commit: a9c5ead on Feb 10, 2021, https://github.com/dotnet/runtime/commit/a9c5eadd951dcba73167f72cc624eb790573663a
+//
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Text;
+
+namespace Avalonia.Utilities;
+
+// Provide a cached reusable instance of stringbuilder per thread.
+internal static class StringBuilderCache
+{
+ // The value 360 was chosen in discussion with performance experts as a compromise between using
+ // as little memory per thread as possible and still covering a large part of short-lived
+ // StringBuilder creations on the startup path of VS designers.
+ internal const int MaxBuilderSize = 360;
+ private const int DefaultCapacity = 16; // == StringBuilder.DefaultCapacity
+
+ // WARNING: We allow diagnostic tools to directly inspect this member (t_cachedInstance).
+ // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details.
+ // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools.
+ // Get in touch with the diagnostics team if you have questions.
+ [ThreadStatic]
+ private static StringBuilder? t_cachedInstance;
+
+ /// Get a StringBuilder for the specified capacity.
+ /// If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied.
+ public static StringBuilder Acquire(int capacity = DefaultCapacity)
+ {
+ if (capacity <= MaxBuilderSize)
+ {
+ StringBuilder? sb = t_cachedInstance;
+ if (sb != null)
+ {
+ // Avoid stringbuilder block fragmentation by getting a new StringBuilder
+ // when the requested size is larger than the current capacity
+ if (capacity <= sb.Capacity)
+ {
+ t_cachedInstance = null;
+ sb.Clear();
+ return sb;
+ }
+ }
+ }
+
+ return new StringBuilder(capacity);
+ }
+
+ /// Place the specified builder in the cache if it is not too big.
+ public static void Release(StringBuilder sb)
+ {
+ if (sb.Capacity <= MaxBuilderSize)
+ {
+ t_cachedInstance = sb;
+ }
+ }
+
+ /// ToString() the stringbuilder, Release it to the cache, and return the resulting string.
+ public static string GetStringAndRelease(StringBuilder sb)
+ {
+ string result = sb.ToString();
+ Release(sb);
+ return result;
+ }
+}
diff --git a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
index eef574d297..8387c62cad 100644
--- a/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
+++ b/src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj
@@ -50,6 +50,7 @@
Markup/%(RecursiveDir)%(FileName)%(Extension)
+
Markup/%(RecursiveDir)%(FileName)%(Extension)
diff --git a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelper.cs b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelper.cs
index 7dc340ea16..c1a03b1b77 100644
--- a/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelper.cs
+++ b/src/Avalonia.Controls.ColorPicker/Helpers/ColorHelper.cs
@@ -2,7 +2,7 @@
using System.Globalization;
using System.Collections.Generic;
using Avalonia.Media;
-using System.Text;
+using Avalonia.Utilities;
namespace Avalonia.Controls.Primitives
{
@@ -113,7 +113,7 @@ namespace Avalonia.Controls.Primitives
// Cache results for next time as well
if (closestKnownColor != KnownColor.None)
{
- StringBuilder sb = new StringBuilder();
+ var sb = StringBuilderCache.Acquire();
string name = closestKnownColor.ToString();
// Add spaces converting PascalCase to human-readable names
@@ -128,7 +128,7 @@ namespace Avalonia.Controls.Primitives
sb.Append(name[i]);
}
- string displayName = sb.ToString();
+ string displayName = StringBuilderCache.GetStringAndRelease(sb);
lock (cacheMutex)
{
diff --git a/src/Avalonia.Controls.DataGrid/DataGrid.cs b/src/Avalonia.Controls.DataGrid/DataGrid.cs
index cacd5c5a16..c6cc9bf278 100644
--- a/src/Avalonia.Controls.DataGrid/DataGrid.cs
+++ b/src/Avalonia.Controls.DataGrid/DataGrid.cs
@@ -6020,7 +6020,7 @@ namespace Avalonia.Controls
/// The formatted string.
private string FormatClipboardContent(DataGridRowClipboardEventArgs e)
{
- var text = new StringBuilder();
+ var text = StringBuilderCache.Acquire();
var clipboardRowContent = e.ClipboardRowContent;
var numberOfItem = clipboardRowContent.Count;
for (int cellIndex = 0; cellIndex < numberOfItem; cellIndex++)
@@ -6037,7 +6037,7 @@ namespace Avalonia.Controls
text.Append('\n');
}
}
- return text.ToString();
+ return StringBuilderCache.GetStringAndRelease(text);
}
///
@@ -6052,7 +6052,7 @@ namespace Avalonia.Controls
if (ctrl && !shift && !alt && ClipboardCopyMode != DataGridClipboardCopyMode.None && SelectedItems.Count > 0)
{
- StringBuilder textBuilder = new StringBuilder();
+ var textBuilder = StringBuilderCache.Acquire();
if (ClipboardCopyMode == DataGridClipboardCopyMode.IncludeHeader)
{
@@ -6078,7 +6078,7 @@ namespace Avalonia.Controls
textBuilder.Append(FormatClipboardContent(itemArgs));
}
- string text = textBuilder.ToString();
+ string text = StringBuilderCache.GetStringAndRelease(textBuilder);
if (!string.IsNullOrEmpty(text))
{
diff --git a/src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs b/src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs
index 9a657cce68..0fa43809ac 100644
--- a/src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs
+++ b/src/Avalonia.Controls/Converters/PlatformKeyGestureConverter.cs
@@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
using System.Text;
using Avalonia.Data.Converters;
using Avalonia.Input;
+using Avalonia.Utilities;
namespace Avalonia.Controls.Converters
{
@@ -62,7 +63,7 @@ namespace Avalonia.Controls.Converters
private static string ToString(KeyGesture gesture, string meta)
{
- var s = new StringBuilder();
+ var s = StringBuilderCache.Acquire();
static void Plus(StringBuilder s)
{
@@ -98,12 +99,12 @@ namespace Avalonia.Controls.Converters
Plus(s);
s.Append(ToString(gesture.Key));
- return s.ToString();
+ return StringBuilderCache.GetStringAndRelease(s);
}
private static string ToOSXString(KeyGesture gesture)
{
- var s = new StringBuilder();
+ var s = StringBuilderCache.Acquire();
if (gesture.KeyModifiers.HasAllFlags(KeyModifiers.Control))
{
@@ -127,7 +128,7 @@ namespace Avalonia.Controls.Converters
s.Append(ToOSXString(gesture.Key));
- return s.ToString();
+ return StringBuilderCache.GetStringAndRelease(s);
}
private static string ToString(Key key)
diff --git a/src/Avalonia.Controls/Documents/InlineCollection.cs b/src/Avalonia.Controls/Documents/InlineCollection.cs
index 565ed75ad9..190373169b 100644
--- a/src/Avalonia.Controls/Documents/InlineCollection.cs
+++ b/src/Avalonia.Controls/Documents/InlineCollection.cs
@@ -1,8 +1,8 @@
using System;
-using System.Text;
using Avalonia.Collections;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
+using Avalonia.Utilities;
namespace Avalonia.Controls.Documents
{
@@ -70,14 +70,14 @@ namespace Avalonia.Controls.Documents
{
get
{
- var builder = new StringBuilder();
+ var builder = StringBuilderCache.Acquire();
foreach (var inline in this)
{
inline.AppendText(builder);
}
- return builder.ToString();
+ return StringBuilderCache.GetStringAndRelease(builder);
}
}
diff --git a/src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs b/src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs
index 4adcd32302..13c8e070e8 100644
--- a/src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs
+++ b/src/Avalonia.Diagnostics/Diagnostics/VisualTreeDebug.cs
@@ -2,6 +2,7 @@ using System;
using System.Text;
using Avalonia.Controls;
using Avalonia.Data;
+using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics
@@ -10,9 +11,9 @@ namespace Avalonia.Diagnostics
{
public static string PrintVisualTree(IVisual visual)
{
- StringBuilder result = new StringBuilder();
+ var result = StringBuilderCache.Acquire();
PrintVisualTree(visual, result, 0);
- return result.ToString();
+ return StringBuilderCache.GetStringAndRelease(result);
}
private static void PrintVisualTree(IVisual visual, StringBuilder builder, int indent)
diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
index b89ea8399a..ce931b9c14 100644
--- a/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml.Loader/Avalonia.Markup.Xaml.Loader.csproj
@@ -7,6 +7,9 @@
$(DefineConstants);XAMLX_INTERNAL
+
+
+
diff --git a/src/Windows/Avalonia.Win32/ClipboardFormats.cs b/src/Windows/Avalonia.Win32/ClipboardFormats.cs
index 7538dedfca..7bd7765f8c 100644
--- a/src/Windows/Avalonia.Win32/ClipboardFormats.cs
+++ b/src/Windows/Avalonia.Win32/ClipboardFormats.cs
@@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
-using System.Text;
using Avalonia.Input;
using Avalonia.Win32.Interop;
+using Avalonia.Utilities;
namespace Avalonia.Win32
{
@@ -35,9 +35,9 @@ namespace Avalonia.Win32
private static string QueryFormatName(ushort format)
{
- StringBuilder sb = new StringBuilder(MAX_FORMAT_NAME_LENGTH);
+ var sb = StringBuilderCache.Acquire(MAX_FORMAT_NAME_LENGTH);
if (UnmanagedMethods.GetClipboardFormatName(format, sb, sb.Capacity) > 0)
- return sb.ToString();
+ return StringBuilderCache.GetStringAndRelease(sb);
return null;
}
diff --git a/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs b/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
index 1258bb0109..7e1e22579b 100644
--- a/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
+++ b/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
@@ -1,6 +1,6 @@
-using System.Text;
using Avalonia.Controls;
using Avalonia.Input;
+using Avalonia.Utilities;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32.Input
@@ -49,7 +49,7 @@ namespace Avalonia.Win32.Input
public string StringFromVirtualKey(uint virtualKey)
{
- StringBuilder result = new StringBuilder(256);
+ var result = StringBuilderCache.Acquire(256);
int length = UnmanagedMethods.ToUnicode(
virtualKey,
0,
@@ -57,7 +57,7 @@ namespace Avalonia.Win32.Input
result,
256,
0);
- return result.ToString();
+ return StringBuilderCache.GetStringAndRelease(result);
}
private void UpdateKeyStates()
diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs
index ba17177473..f7345b3ff7 100644
--- a/src/Windows/Avalonia.Win32/OleDataObject.cs
+++ b/src/Windows/Avalonia.Win32/OleDataObject.cs
@@ -6,9 +6,9 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization.Formatters.Binary;
-using System.Text;
using Avalonia.Input;
using Avalonia.MicroCom;
+using Avalonia.Utilities;
using Avalonia.Win32.Interop;
using IDataObject = Avalonia.Input.IDataObject;
@@ -103,11 +103,11 @@ namespace Avalonia.Win32
for (int i = 0; i < fileCount; i++)
{
int pathLen = UnmanagedMethods.DragQueryFile(hGlobal, i, null, 0);
- StringBuilder sb = new StringBuilder(pathLen+1);
+ var sb = StringBuilderCache.Acquire(pathLen+1);
if (UnmanagedMethods.DragQueryFile(hGlobal, i, sb, sb.Capacity) == pathLen)
{
- files.Add(sb.ToString());
+ files.Add(StringBuilderCache.GetStringAndRelease(sb));
}
}
}
diff --git a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
index f509bb21ba..dadef1cb9c 100644
--- a/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
+++ b/src/tools/Avalonia.Designer.HostApp/Avalonia.Designer.HostApp.csproj
@@ -16,6 +16,7 @@
+