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

Commit 2ee8405

Browse files
committed
Add JsConfig.ReuseStringBuffer option for better perf when serialializing to a JSON string
1 parent a080405 commit 2ee8405

File tree

3 files changed

+115
-31
lines changed

3 files changed

+115
-31
lines changed

src/ServiceStack.Text/JsConfig.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public static JsConfigScope With(
5050
bool? appendUtcOffset = null,
5151
bool? escapeUnicode = null,
5252
bool? includePublicFields = null,
53+
bool? reuseStringBuffer = null,
5354
int? maxDepth = null,
5455
EmptyCtorFactoryDelegate modelFactory = null,
5556
string[] excludePropertyReferences = null)
@@ -78,6 +79,7 @@ public static JsConfigScope With(
7879
AppendUtcOffset = appendUtcOffset ?? sAppendUtcOffset,
7980
EscapeUnicode = escapeUnicode ?? sEscapeUnicode,
8081
IncludePublicFields = includePublicFields ?? sIncludePublicFields,
82+
ReuseStringBuffer = reuseStringBuffer ?? sReuseStringBuffer,
8183
MaxDepth = maxDepth ?? sMaxDepth,
8284
ModelFactory = modelFactory ?? ModelFactory,
8385
ExcludePropertyReferences = excludePropertyReferences ?? sExcludePropertyReferences
@@ -522,6 +524,25 @@ public static bool IncludePublicFields
522524
}
523525
}
524526

527+
/// <summary>
528+
/// For extra serialization performance you can re-use a ThreadStatic StringBuilder
529+
/// when serializing to a JSON String.
530+
/// </summary>
531+
private static bool? sReuseStringBuffer;
532+
public static bool ReuseStringBuffer
533+
{
534+
get
535+
{
536+
return (JsConfigScope.Current != null ? JsConfigScope.Current.ReuseStringBuffer : null)
537+
?? sReuseStringBuffer
538+
?? false;
539+
}
540+
set
541+
{
542+
if (!sReuseStringBuffer.HasValue) sReuseStringBuffer = value;
543+
}
544+
}
545+
525546
/// <summary>
526547
/// Sets the maximum depth to avoid circular dependencies
527548
/// </summary>
@@ -622,6 +643,7 @@ public static void Reset()
622643
sAppendUtcOffset = null;
623644
sEscapeUnicode = null;
624645
sIncludePublicFields = null;
646+
sReuseStringBuffer = null;
625647
HasSerializeFn = new HashSet<Type>();
626648
TreatValueAsRefTypes = new HashSet<Type> { typeof(KeyValuePair<,>) };
627649
sPropertyConvention = null;

src/ServiceStack.Text/JsConfigScope.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public void Dispose()
7777
public bool? EscapeUnicode { get; set; }
7878
public bool? PreferInterfaces { get; set; }
7979
public bool? IncludePublicFields { get; set; }
80+
public bool? ReuseStringBuffer { get; set; }
8081
public int? MaxDepth { get; set; }
8182
public EmptyCtorFactoryDelegate ModelFactory { get; set; }
8283
public string[] ExcludePropertyReferences { get; set; }

src/ServiceStack.Text/JsonSerializer.cs

Lines changed: 92 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -50,49 +50,101 @@ public static object DeserializeFromReader(TextReader reader, Type type)
5050
return DeserializeFromString(reader.ReadToEnd(), type);
5151
}
5252

53-
public static string SerializeToString<T>(T value)
53+
[ThreadStatic] //Reuse the thread static StringBuilder when serializing to strings
54+
private static StringBuilderWriter LastWriter;
55+
56+
internal class StringBuilderWriter : IDisposable
57+
{
58+
protected StringBuilder sb;
59+
protected StringWriter writer;
60+
61+
public StringWriter Writer
62+
{
63+
get { return writer; }
64+
}
65+
66+
public StringBuilderWriter()
67+
{
68+
this.sb = new StringBuilder();
69+
this.writer = new StringWriter(sb, CultureInfo.InvariantCulture);
70+
}
71+
72+
public static StringBuilderWriter Create()
73+
{
74+
var ret = LastWriter;
75+
if (JsConfig.ReuseStringBuffer && ret != null)
76+
{
77+
LastWriter = null;
78+
ret.sb.Clear();
79+
return ret;
80+
}
81+
82+
return new StringBuilderWriter();
83+
}
84+
85+
public override string ToString()
86+
{
87+
return sb.ToString();
88+
}
89+
90+
public void Dispose()
91+
{
92+
if (JsConfig.ReuseStringBuffer)
93+
{
94+
LastWriter = this;
95+
}
96+
else
97+
{
98+
Writer.Dispose();
99+
}
100+
}
101+
}
102+
103+
public static string SerializeToString<T>(T value)
54104
{
55105
if (value == null || value is Delegate) return null;
56-
if (typeof(T) == typeof(object) || typeof(T).IsAbstract() || typeof(T).IsInterface())
106+
if (typeof(T) == typeof(object))
107+
{
108+
return SerializeToString(value, value.GetType());
109+
}
110+
if (typeof(T).IsAbstract() || typeof(T).IsInterface())
57111
{
58-
if (typeof(T).IsAbstract() || typeof(T).IsInterface()) JsState.IsWritingDynamic = true;
112+
JsState.IsWritingDynamic = true;
59113
var result = SerializeToString(value, value.GetType());
60-
if (typeof(T).IsAbstract() || typeof(T).IsInterface()) JsState.IsWritingDynamic = false;
114+
JsState.IsWritingDynamic = false;
61115
return result;
62116
}
63117

64-
var sb = new StringBuilder();
65-
using (var writer = new StringWriter(sb, CultureInfo.InvariantCulture))
118+
using (var sb = StringBuilderWriter.Create())
66119
{
67120
if (typeof(T) == typeof(string))
68121
{
69-
JsonUtils.WriteString(writer, value as string);
122+
JsonUtils.WriteString(sb.Writer, value as string);
70123
}
71124
else
72125
{
73-
JsonWriter<T>.WriteRootObject(writer, value);
126+
JsonWriter<T>.WriteRootObject(sb.Writer, value);
74127
}
128+
return sb.ToString();
75129
}
76-
return sb.ToString();
77130
}
78131

79132
public static string SerializeToString(object value, Type type)
80133
{
81134
if (value == null) return null;
82135

83-
var sb = new StringBuilder();
84-
using (var writer = new StringWriter(sb, CultureInfo.InvariantCulture))
85-
{
136+
using (var sb = StringBuilderWriter.Create())
137+
{
86138
if (type == typeof(string))
87139
{
88-
JsonUtils.WriteString(writer, value as string);
140+
JsonUtils.WriteString(sb.Writer, value as string);
89141
}
90142
else
91143
{
92-
JsonWriter.GetWriteFn(type)(writer, value);
144+
JsonWriter.GetWriteFn(type)(sb.Writer, value);
93145
}
94-
}
95-
return sb.ToString();
146+
return sb.ToString();
147+
}
96148
}
97149

98150
public static void SerializeToWriter<T>(T value, TextWriter writer)
@@ -101,17 +153,21 @@ public static void SerializeToWriter<T>(T value, TextWriter writer)
101153
if (typeof(T) == typeof(string))
102154
{
103155
writer.Write(value);
104-
return;
105156
}
106-
if (typeof(T) == typeof(object) || typeof(T).IsAbstract() || typeof(T).IsInterface())
157+
else if (typeof(T) == typeof(object))
107158
{
108-
if (typeof(T).IsAbstract() || typeof(T).IsInterface()) JsState.IsWritingDynamic = true;
109159
SerializeToWriter(value, value.GetType(), writer);
110-
if (typeof(T).IsAbstract() || typeof(T).IsInterface()) JsState.IsWritingDynamic = false;
111-
return;
112160
}
113-
114-
JsonWriter<T>.WriteRootObject(writer, value);
161+
else if (typeof(T).IsAbstract() || typeof(T).IsInterface())
162+
{
163+
JsState.IsWritingDynamic = false;
164+
SerializeToWriter(value, value.GetType(), writer);
165+
JsState.IsWritingDynamic = true;
166+
}
167+
else
168+
{
169+
JsonWriter<T>.WriteRootObject(writer, value);
170+
}
115171
}
116172

117173
public static void SerializeToWriter(object value, Type type, TextWriter writer)
@@ -129,17 +185,22 @@ public static void SerializeToWriter(object value, Type type, TextWriter writer)
129185
public static void SerializeToStream<T>(T value, Stream stream)
130186
{
131187
if (value == null) return;
132-
if (typeof(T) == typeof(object) || typeof(T).IsAbstract() || typeof(T).IsInterface())
188+
if (typeof(T) == typeof(object))
133189
{
134-
if (typeof(T).IsAbstract() || typeof(T).IsInterface()) JsState.IsWritingDynamic = true;
135190
SerializeToStream(value, value.GetType(), stream);
136-
if (typeof(T).IsAbstract() || typeof(T).IsInterface()) JsState.IsWritingDynamic = false;
137-
return;
138191
}
139-
140-
var writer = new StreamWriter(stream, UTF8EncodingWithoutBom);
141-
JsonWriter<T>.WriteRootObject(writer, value);
142-
writer.Flush();
192+
else if (typeof(T).IsAbstract() || typeof(T).IsInterface())
193+
{
194+
JsState.IsWritingDynamic = false;
195+
SerializeToStream(value, value.GetType(), stream);
196+
JsState.IsWritingDynamic = true;
197+
}
198+
else
199+
{
200+
var writer = new StreamWriter(stream, UTF8EncodingWithoutBom);
201+
JsonWriter<T>.WriteRootObject(writer, value);
202+
writer.Flush();
203+
}
143204
}
144205

145206
public static void SerializeToStream(object value, Type type, Stream stream)

0 commit comments

Comments
 (0)