Skip to content

Commit

Permalink
Add CodedInputReader and CodedOutputWriter
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Nov 3, 2019
1 parent 129a7c8 commit d963fd9
Show file tree
Hide file tree
Showing 16 changed files with 4,906 additions and 373 deletions.
683 changes: 676 additions & 7 deletions csharp/src/Google.Protobuf.Benchmarks/BenchmarkMessage1Proto3.cs

Large diffs are not rendered by default.

145 changes: 145 additions & 0 deletions csharp/src/Google.Protobuf.Benchmarks/JamesBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2019 Google Inc. All rights reserved.
// https://github.com/protocolbuffers/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

using BenchmarkDotNet.Attributes;
using Benchmarks.Proto3;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Google.Protobuf.Benchmarks
{
[MemoryDiagnoser]
public class JamesBenchmarks
{
private GoogleMessage1 _message;
private byte[] _messageData;
private int _messageSize;
private BufferWriter _bufferWriter;
private ReadOnlySequence<byte> _readOnlySequence;

[GlobalSetup]
public void GlobalSetup()
{
MemoryStream ms = new MemoryStream();
CodedOutputStream output = new CodedOutputStream(ms);

GoogleMessage1 googleMessage1 = new GoogleMessage1();
googleMessage1.Field1 = "Text" + new string('!', 200);
googleMessage1.Field2 = 2;
googleMessage1.Field15 = new GoogleMessage1SubMessage();
googleMessage1.Field15.Field1 = 1;

googleMessage1.WriteTo(output);
output.Flush();

_message = googleMessage1;
_messageData = ms.ToArray();
_messageSize = googleMessage1.CalculateSize();

_bufferWriter = new BufferWriter(new byte[_messageSize]);
_readOnlySequence = new ReadOnlySequence<byte>(_messageData);
}

[Benchmark]
public void WriteToByteArray()
{
CodedOutputStream output = new CodedOutputStream(new byte[_messageSize]);

_message.WriteTo(output);
}

[Benchmark]
public void ParseFromByteArray()
{
var messageData = new byte[_messageData.Length];
Array.Copy(_messageData, messageData, _messageData.Length);

CodedInputStream input = new CodedInputStream(messageData);

GoogleMessage1 message = new GoogleMessage1();
message.MergeFrom(input);
}

[Benchmark]
public void WriteToBufferWriter()
{
CodedOutputWriter output = new CodedOutputWriter(_bufferWriter);

_message.WriteTo(ref output);

_bufferWriter.Reset();
}

[Benchmark]
public void ParseFromReadOnlySequence()
{
CodedInputReader input = new CodedInputReader(_readOnlySequence);

GoogleMessage1 message = new GoogleMessage1();
message.MergeFrom(ref input);
}
}

internal class BufferWriter : IBufferWriter<byte>
{
private readonly byte[] _buffer;
private int _position;

public BufferWriter(byte[] buffer)
{
_buffer = buffer;
}

public void Advance(int count)
{
_position += count;
}

public void Reset()
{
_position = 0;
}

public Memory<byte> GetMemory(int sizeHint = 0)
{
return _buffer.AsMemory(_position);
}

public Span<byte> GetSpan(int sizeHint = 0)
{
return _buffer.AsSpan(_position);
}
}
}
13 changes: 12 additions & 1 deletion csharp/src/Google.Protobuf.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ namespace Google.Protobuf.Benchmarks
/// </summary>
class Program
{
static void Main() => BenchmarkRunner.Run<SerializationBenchmark>();
#if true
static void Main() => BenchmarkRunner.Run<JamesBenchmarks>();
#else
static void Main()
{
var b = new JamesBenchmarks();
b.GlobalSetup();

b.ParseFromReadOnlySequence();
//b.ParseFromByteArray();
}
#endif
}
}
158 changes: 158 additions & 0 deletions csharp/src/Google.Protobuf.Test/ArrayBufferWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

#if NETCOREAPP2_1
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;

namespace Google.Protobuf
{
internal class ArrayBufferWriter : IBufferWriter<byte>, IDisposable
{
private ResizableArray<byte> _buffer;
private readonly int _maximumSizeHint;

public ArrayBufferWriter(int capacity, int? maximumSizeHint = null)
{
_buffer = new ResizableArray<byte>(ArrayPool<byte>.Shared.Rent(capacity));
_maximumSizeHint = maximumSizeHint ?? int.MaxValue;
}

public int CommitedByteCount => _buffer.Count;

public void Clear()
{
_buffer.Count = 0;
}

public ArraySegment<byte> Free => _buffer.Free;

public ArraySegment<byte> Formatted => _buffer.Full;

public Memory<byte> GetMemory(int minimumLength = 0)
{
minimumLength = Math.Min(minimumLength, _maximumSizeHint);

if (minimumLength < 1)
{
minimumLength = 1;
}

if (minimumLength > _buffer.FreeCount)
{
int doubleCount = _buffer.FreeCount * 2;
int newSize = minimumLength > doubleCount ? minimumLength : doubleCount;
byte[] newArray = ArrayPool<byte>.Shared.Rent(newSize + _buffer.Count);
byte[] oldArray = _buffer.Resize(newArray);
ArrayPool<byte>.Shared.Return(oldArray);
}

return _buffer.FreeMemory;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<byte> GetSpan(int minimumLength = 0)
{
minimumLength = Math.Min(minimumLength, _maximumSizeHint);

if (minimumLength < 1)
{
minimumLength = 1;
}

if (minimumLength > _buffer.FreeCount)
{
int doubleCount = _buffer.FreeCount * 2;
int newSize = minimumLength > doubleCount ? minimumLength : doubleCount;
byte[] newArray = ArrayPool<byte>.Shared.Rent(newSize + _buffer.Count);
byte[] oldArray = _buffer.Resize(newArray);
ArrayPool<byte>.Shared.Return(oldArray);
}

return _buffer.FreeSpan;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Advance(int bytes)
{
_buffer.Count += bytes;
if (_buffer.Count > _buffer.Capacity)
{
throw new InvalidOperationException("More bytes commited than returned from FreeBuffer");
}
}

public void Dispose()
{
byte[] array = _buffer.Array;
_buffer.Array = null;
ArrayPool<byte>.Shared.Return(array);
}

private struct ResizableArray<T>
{
public ResizableArray(T[] array, int count = 0)
{
Array = array;
Count = count;
}

public T[] Array { get; set; }

public int Count { get; set; }

public int Capacity => Array.Length;

public T[] Resize(T[] newArray)
{
T[] oldArray = Array;
Array.AsSpan(0, Count).CopyTo(newArray); // CopyTo will throw if newArray.Length < _count
Array = newArray;
return oldArray;
}

public ArraySegment<T> Full => new ArraySegment<T>(Array, 0, Count);

public ArraySegment<T> Free => new ArraySegment<T>(Array, Count, Array.Length - Count);

public Span<T> FreeSpan => new Span<T>(Array, Count, Array.Length - Count);

public Memory<T> FreeMemory => new Memory<T>(Array, Count, Array.Length - Count);

public int FreeCount => Array.Length - Count;
}
}
}
#endif
Loading

0 comments on commit d963fd9

Please sign in to comment.