-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Expose Serialize(StringBuilder) overload (#82)
Co-authored-by: Rolf Kristensen <sweaty1@hotmail.com>
- Loading branch information
Showing
8 changed files
with
247 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
src/Elastic.CommonSchema/Serialization/EcsSerializerFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using System; | ||
using System.Buffers; | ||
using System.IO; | ||
using System.Text.Json; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Elastic.CommonSchema.Serialization | ||
{ | ||
/// <summary> | ||
/// This static class allows you to deserialize subclasses of <see cref="Base"/> | ||
/// If you are dealing with <see cref="Base"/> directly you do not need to use this class, | ||
/// use <see cref="Base.Deserialize(string)"/> and the overloads instead. | ||
/// </summary> | ||
/// <remarks> | ||
/// This class should only be used for advanced use cases, for simpler use cases you can utilise the <see cref="Base.Metadata"/> property. | ||
/// </remarks> | ||
/// <typeparam name="TBase">Type of the <see cref="Base"/> subclass</typeparam> | ||
public static class EcsSerializerFactory<TBase> where TBase : Base, new() | ||
{ | ||
public static ValueTask<TBase> DeserializeAsync(Stream stream, CancellationToken ctx = default) => | ||
JsonSerializer.DeserializeAsync<TBase>(stream, JsonConfiguration.SerializerOptions, ctx); | ||
|
||
public static TBase Deserialize(string json) => JsonSerializer.Deserialize<TBase>(json, JsonConfiguration.SerializerOptions); | ||
|
||
public static TBase Deserialize(ReadOnlySpan<byte> json) => JsonSerializer.Deserialize<TBase>(json, JsonConfiguration.SerializerOptions); | ||
|
||
public static TBase Deserialize(Stream stream) | ||
{ | ||
using var ms = new MemoryStream(); | ||
var buffer = ArrayPool<byte>.Shared.Rent(1024); | ||
var total = 0; | ||
int read; | ||
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) | ||
{ | ||
ms.Write(buffer, 0, read); | ||
total += read; | ||
} | ||
var span = ms.TryGetBuffer(out var segment) | ||
? new ReadOnlyMemory<byte>(segment.Array, segment.Offset, total).Span | ||
: new ReadOnlyMemory<byte>(ms.ToArray()).Span; | ||
return Deserialize(span); | ||
} | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
src/Elastic.CommonSchema/Serialization/ReusableUtf8JsonWriter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Licensed to Elasticsearch B.V under one or more agreements. | ||
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
// See the LICENSE file in the project root for more information | ||
|
||
using System; | ||
using System.IO; | ||
using System.Text; | ||
using System.Text.Json; | ||
|
||
namespace Elastic.CommonSchema.Serialization | ||
{ | ||
internal sealed class ReusableUtf8JsonWriter | ||
{ | ||
private Utf8JsonWriter _cachedJsonWriter; | ||
private readonly MemoryStream _cachedMemoryStream; | ||
private readonly char[] _cachedEncodingBuffer; | ||
|
||
public ReusableUtf8JsonWriter() | ||
{ | ||
_cachedMemoryStream = new MemoryStream(4 * 1024); | ||
_cachedJsonWriter = new Utf8JsonWriter(_cachedMemoryStream); | ||
_cachedEncodingBuffer = new char[1024]; | ||
} | ||
|
||
public ReusableJsonWriter AllocateJsonWriter(StringBuilder text) | ||
{ | ||
var writer = System.Threading.Interlocked.Exchange(ref _cachedJsonWriter, null); | ||
return new ReusableJsonWriter(this, writer, text); | ||
} | ||
|
||
public ReusableJsonWriter NewJsonWriter(StringBuilder text) => | ||
new ReusableJsonWriter(this, new Utf8JsonWriter(new MemoryStream()), text); | ||
|
||
private void Return(Utf8JsonWriter writer, StringBuilder output) | ||
{ | ||
writer.Flush(); | ||
|
||
if (_cachedMemoryStream.Length > 0) | ||
{ | ||
if (!_cachedMemoryStream.TryGetBuffer(out var byteArray)) | ||
byteArray = new ArraySegment<byte>(_cachedMemoryStream.GetBuffer(), 0, (int)_cachedMemoryStream.Length); | ||
|
||
CopyToStringBuilder(byteArray, _cachedEncodingBuffer, output); | ||
} | ||
|
||
writer.Reset(); | ||
_cachedMemoryStream.Position = 0; | ||
_cachedMemoryStream.SetLength(0); | ||
System.Threading.Interlocked.Exchange(ref _cachedJsonWriter, writer); | ||
} | ||
|
||
private static void CopyToStringBuilder(ArraySegment<byte> byteArray, char[] encodingBuffer, StringBuilder output) | ||
{ | ||
for (var i = 0; i < byteArray.Count; i += encodingBuffer.Length) | ||
{ | ||
var byteCount = Math.Min(byteArray.Count - i, encodingBuffer.Length); | ||
var charCount = Encoding.UTF8.GetChars(byteArray.Array, byteArray.Offset + i, byteCount, encodingBuffer, 0); | ||
output.Append(encodingBuffer, 0, charCount); | ||
} | ||
} | ||
|
||
internal readonly struct ReusableJsonWriter : IDisposable | ||
{ | ||
private readonly ReusableUtf8JsonWriter _owner; | ||
private readonly Utf8JsonWriter _writer; | ||
private readonly StringBuilder _output; | ||
|
||
public ReusableJsonWriter(ReusableUtf8JsonWriter owner, Utf8JsonWriter writer, StringBuilder output) | ||
{ | ||
_writer = writer; | ||
_owner = writer != null ? owner : null; | ||
_output = output; | ||
} | ||
|
||
public void Serialize(Base ecsEvent) | ||
{ | ||
if (_writer != null) | ||
ecsEvent.Serialize(_writer); | ||
else | ||
{ | ||
var result = ecsEvent.Serialize(); | ||
_output.Append(result); | ||
} | ||
} | ||
|
||
public void Dispose() => _owner?.Return(_writer, _output); | ||
} | ||
} | ||
} |
10 changes: 5 additions & 5 deletions
10
tests/Elastic.CommonSchema.Benchmarks/Elastic.CommonSchema.Benchmarks.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.bat))\src\Library.build.props"/> | ||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), build.bat))\src\Library.build.props" /> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>netcoreapp3.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="AutoBogus" Version="2.8.2"/> | ||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1"/> | ||
<PackageReference Include="Bogus" Version="29.0.2"/> | ||
<PackageReference Include="AutoBogus" Version="2.8.2" /> | ||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" /> | ||
<PackageReference Include="Bogus" Version="29.0.2" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Elastic.CommonSchema\Elastic.CommonSchema.csproj"/> | ||
<ProjectReference Include="..\..\src\Elastic.CommonSchema\Elastic.CommonSchema.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
70 changes: 70 additions & 0 deletions
70
tests/Elastic.CommonSchema.Benchmarks/SerializingStringBuilderBase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
using System; | ||
using System.Text; | ||
using AutoBogus; | ||
using BenchmarkDotNet.Attributes; | ||
|
||
namespace Elastic.CommonSchema.Benchmarks | ||
{ | ||
[UnicodeConsoleLogger, MemoryDiagnoser, ThreadingDiagnoser] | ||
public class SerializingStringBuilderBase | ||
{ | ||
[Benchmark] | ||
public StringBuilder Empty() | ||
{ | ||
var ecs = new Base(); | ||
return ecs.Serialize(new StringBuilder()); | ||
} | ||
[Benchmark] | ||
public StringBuilder Minimal() | ||
{ | ||
var ecs = new Base | ||
{ | ||
Timestamp = DateTimeOffset.UtcNow, | ||
Log = new Log | ||
{ | ||
Level = "Debug" | ||
}, | ||
Message = "hello world!" | ||
}; | ||
return ecs.Serialize(new StringBuilder()); | ||
} | ||
[Benchmark] | ||
public StringBuilder Complex() | ||
{ | ||
var ecs = new Base | ||
{ | ||
Timestamp = DateTimeOffset.UtcNow, | ||
Log = new Log | ||
{ | ||
Level = "Debug", Logger = "Logger", | ||
Origin = new LogOrigin | ||
{ | ||
File = new OriginFile { Line = 12, Name = "file.cs"}, Function = "Complex" | ||
}, | ||
Original = "new log line", | ||
Syslog = new LogSyslog { | ||
Facility = new SyslogFacility | ||
{ | ||
Code = 12, Name = "syslog" | ||
}, Priority = 12, Severity = new SyslogSeverity() | ||
{ | ||
Code = 12, Name = "asd" | ||
}, | ||
} | ||
}, | ||
Message = "hello world!", | ||
Agent = new Agent | ||
{ | ||
Name = "test" | ||
} | ||
|
||
}; | ||
return ecs.Serialize(new StringBuilder()); | ||
} | ||
|
||
public static readonly Base FullInstance = new AutoFaker<Base>().Generate(); | ||
|
||
[Benchmark] | ||
public StringBuilder Full() => FullInstance.Serialize(new StringBuilder()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters