CodedOutputStream adjustments

pull/7576/head
Jan Tattermusch 5 years ago
parent f9d9019e27
commit ee6b20afbe
  1. 4
      csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs
  2. 522
      csharp/src/Google.Protobuf/CodedOutputStream.cs
  3. 4
      csharp/src/Google.Protobuf/Collections/RepeatedField.cs
  4. 2
      csharp/src/Google.Protobuf/MessageExtensions.cs

@ -208,7 +208,7 @@ namespace Google.Protobuf
/// </summary>
public static int ComputeSInt32Size(int value)
{
return ComputeRawVarint32Size(EncodeZigZag32(value));
return ComputeRawVarint32Size(WritingPrimitives.EncodeZigZag32(value));
}
/// <summary>
@ -217,7 +217,7 @@ namespace Google.Protobuf
/// </summary>
public static int ComputeSInt64Size(long value)
{
return ComputeRawVarint64Size(EncodeZigZag64(value));
return ComputeRawVarint64Size(WritingPrimitives.EncodeZigZag64(value));
}
/// <summary>

@ -67,8 +67,8 @@ namespace Google.Protobuf
private readonly bool leaveOpen;
private readonly byte[] buffer;
private readonly int limit;
private int position;
private WriterInternalState state;
private readonly Stream output;
#region Construction
@ -90,8 +90,9 @@ namespace Google.Protobuf
{
this.output = null;
this.buffer = ProtoPreconditions.CheckNotNull(buffer, nameof(buffer));
this.position = offset;
this.limit = offset + length;
this.state.position = offset;
this.state.limit = offset + length;
WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);
leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
}
@ -99,8 +100,9 @@ namespace Google.Protobuf
{
this.output = ProtoPreconditions.CheckNotNull(output, nameof(output));
this.buffer = buffer;
this.position = 0;
this.limit = buffer.Length;
this.state.position = 0;
this.state.limit = buffer.Length;
WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);
this.leaveOpen = leaveOpen;
}
@ -155,9 +157,9 @@ namespace Google.Protobuf
{
if (output != null)
{
return output.Position + position;
return output.Position + state.position;
}
return position;
return state.position;
}
}
@ -169,7 +171,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteDouble(double value)
{
WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
var span = new Span<byte>(buffer);
WritingPrimitives.WriteDouble(ref span, ref state, value);
}
/// <summary>
@ -178,23 +181,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteFloat(float value)
{
byte[] rawBytes = BitConverter.GetBytes(value);
if (!BitConverter.IsLittleEndian)
{
ByteArray.Reverse(rawBytes);
}
if (limit - position >= 4)
{
buffer[position++] = rawBytes[0];
buffer[position++] = rawBytes[1];
buffer[position++] = rawBytes[2];
buffer[position++] = rawBytes[3];
}
else
{
WriteRawBytes(rawBytes, 0, 4);
}
var span = new Span<byte>(buffer);
WritingPrimitives.WriteFloat(ref span, ref state, value);
}
/// <summary>
@ -203,7 +191,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteUInt64(ulong value)
{
WriteRawVarint64(value);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteUInt64(ref span, ref state, value);
}
/// <summary>
@ -212,7 +201,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteInt64(long value)
{
WriteRawVarint64((ulong) value);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteInt64(ref span, ref state, value);
}
/// <summary>
@ -221,15 +211,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteInt32(int value)
{
if (value >= 0)
{
WriteRawVarint32((uint) value);
}
else
{
// Must sign-extend.
WriteRawVarint64((ulong) value);
}
var span = new Span<byte>(buffer);
WritingPrimitives.WriteInt32(ref span, ref state, value);
}
/// <summary>
@ -238,7 +221,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteFixed64(ulong value)
{
WriteRawLittleEndian64(value);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteFixed64(ref span, ref state, value);
}
/// <summary>
@ -247,7 +231,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteFixed32(uint value)
{
WriteRawLittleEndian32(value);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteFixed32(ref span, ref state, value);
}
/// <summary>
@ -256,7 +241,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteBool(bool value)
{
WriteRawByte(value ? (byte) 1 : (byte) 0);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteBool(ref span, ref state, value);
}
/// <summary>
@ -266,30 +252,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteString(string value)
{
// Optimise the case where we have enough space to write
// the string directly to the buffer, which should be common.
int length = Utf8Encoding.GetByteCount(value);
WriteLength(length);
if (limit - position >= length)
{
if (length == value.Length) // Must be all ASCII...
{
for (int i = 0; i < length; i++)
{
buffer[position + i] = (byte)value[i];
}
}
else
{
Utf8Encoding.GetBytes(value, 0, value.Length, buffer, position);
}
position += length;
}
else
{
byte[] bytes = Utf8Encoding.GetBytes(value);
WriteRawBytes(bytes);
}
var span = new Span<byte>(buffer);
WritingPrimitives.WriteString(ref span, ref state, value);
}
/// <summary>
@ -299,6 +263,7 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteMessage(IMessage value)
{
// TOOD: implement....
WriteLength(value.CalculateSize());
value.WriteTo(this);
}
@ -309,6 +274,7 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteGroup(IMessage value)
{
// TODO: implement...
value.WriteTo(this);
}
@ -319,8 +285,9 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteBytes(ByteString value)
{
WriteLength(value.Length);
value.WriteRawBytesTo(this);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteLength(ref span, ref state, value.Length);
WritingPrimitives.WriteBytes(ref span, ref state, value);
}
/// <summary>
@ -329,7 +296,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteUInt32(uint value)
{
WriteRawVarint32(value);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteUInt32(ref span, ref state, value);
}
/// <summary>
@ -338,7 +306,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteEnum(int value)
{
WriteInt32(value);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteEnum(ref span, ref state, value);
}
/// <summary>
@ -347,7 +316,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write.</param>
public void WriteSFixed32(int value)
{
WriteRawLittleEndian32((uint) value);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteSFixed32(ref span, ref state, value);
}
/// <summary>
@ -356,7 +326,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteSFixed64(long value)
{
WriteRawLittleEndian64((ulong) value);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteSFixed64(ref span, ref state, value);
}
/// <summary>
@ -365,7 +336,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteSInt32(int value)
{
WriteRawVarint32(EncodeZigZag32(value));
var span = new Span<byte>(buffer);
WritingPrimitives.WriteSInt32(ref span, ref state, value);
}
/// <summary>
@ -374,7 +346,8 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param>
public void WriteSInt64(long value)
{
WriteRawVarint64(EncodeZigZag64(value));
var span = new Span<byte>(buffer);
WritingPrimitives.WriteSInt64(ref span, ref state, value);
}
/// <summary>
@ -386,7 +359,8 @@ namespace Google.Protobuf
/// <param name="length">Length value, in bytes.</param>
public void WriteLength(int length)
{
WriteRawVarint32((uint) length);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteLength(ref span, ref state, length);
}
#endregion
@ -399,7 +373,8 @@ namespace Google.Protobuf
/// <param name="type">The wire format type of the tag to write</param>
public void WriteTag(int fieldNumber, WireFormat.WireType type)
{
WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
var span = new Span<byte>(buffer);
WritingPrimitives.WriteTag(ref span, ref state, fieldNumber, type);
}
/// <summary>
@ -408,7 +383,8 @@ namespace Google.Protobuf
/// <param name="tag">The encoded tag</param>
public void WriteTag(uint tag)
{
WriteRawVarint32(tag);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteTag(ref span, ref state, tag);
}
/// <summary>
@ -417,7 +393,8 @@ namespace Google.Protobuf
/// <param name="b1">The encoded tag</param>
public void WriteRawTag(byte b1)
{
WriteRawByte(b1);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteRawTag(ref span, ref state, b1);
}
/// <summary>
@ -427,8 +404,8 @@ namespace Google.Protobuf
/// <param name="b2">The second byte of the encoded tag</param>
public void WriteRawTag(byte b1, byte b2)
{
WriteRawByte(b1);
WriteRawByte(b2);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2);
}
/// <summary>
@ -439,9 +416,8 @@ namespace Google.Protobuf
/// <param name="b3">The third byte of the encoded tag</param>
public void WriteRawTag(byte b1, byte b2, byte b3)
{
WriteRawByte(b1);
WriteRawByte(b2);
WriteRawByte(b3);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3);
}
/// <summary>
@ -453,10 +429,8 @@ namespace Google.Protobuf
/// <param name="b4">The fourth byte of the encoded tag</param>
public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
{
WriteRawByte(b1);
WriteRawByte(b2);
WriteRawByte(b3);
WriteRawByte(b4);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4);
}
/// <summary>
@ -469,130 +443,128 @@ namespace Google.Protobuf
/// <param name="b5">The fifth byte of the encoded tag</param>
public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
{
WriteRawByte(b1);
WriteRawByte(b2);
WriteRawByte(b3);
WriteRawByte(b4);
WriteRawByte(b5);
var span = new Span<byte>(buffer);
WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4, b5);
}
#endregion
#region Underlying writing primitives
/// <summary>
/// Writes a 32 bit value as a varint. The fast route is taken when
/// there's enough buffer space left to whizz through without checking
/// for each byte; otherwise, we resort to calling WriteRawByte each time.
/// </summary>
internal void WriteRawVarint32(uint value)
{
// Optimize for the common case of a single byte value
if (value < 128 && position < limit)
{
buffer[position++] = (byte)value;
return;
}
while (value > 127 && position < limit)
{
buffer[position++] = (byte) ((value & 0x7F) | 0x80);
value >>= 7;
}
while (value > 127)
{
WriteRawByte((byte) ((value & 0x7F) | 0x80));
value >>= 7;
}
if (position < limit)
{
buffer[position++] = (byte) value;
}
else
{
WriteRawByte((byte) value);
}
}
internal void WriteRawVarint64(ulong value)
{
while (value > 127 && position < limit)
{
buffer[position++] = (byte) ((value & 0x7F) | 0x80);
value >>= 7;
}
while (value > 127)
{
WriteRawByte((byte) ((value & 0x7F) | 0x80));
value >>= 7;
}
if (position < limit)
{
buffer[position++] = (byte) value;
}
else
{
WriteRawByte((byte) value);
}
}
internal void WriteRawLittleEndian32(uint value)
{
if (position + 4 > limit)
{
WriteRawByte((byte) value);
WriteRawByte((byte) (value >> 8));
WriteRawByte((byte) (value >> 16));
WriteRawByte((byte) (value >> 24));
}
else
{
buffer[position++] = ((byte) value);
buffer[position++] = ((byte) (value >> 8));
buffer[position++] = ((byte) (value >> 16));
buffer[position++] = ((byte) (value >> 24));
}
}
internal void WriteRawLittleEndian64(ulong value)
{
if (position + 8 > limit)
{
WriteRawByte((byte) value);
WriteRawByte((byte) (value >> 8));
WriteRawByte((byte) (value >> 16));
WriteRawByte((byte) (value >> 24));
WriteRawByte((byte) (value >> 32));
WriteRawByte((byte) (value >> 40));
WriteRawByte((byte) (value >> 48));
WriteRawByte((byte) (value >> 56));
}
else
{
buffer[position++] = ((byte) value);
buffer[position++] = ((byte) (value >> 8));
buffer[position++] = ((byte) (value >> 16));
buffer[position++] = ((byte) (value >> 24));
buffer[position++] = ((byte) (value >> 32));
buffer[position++] = ((byte) (value >> 40));
buffer[position++] = ((byte) (value >> 48));
buffer[position++] = ((byte) (value >> 56));
}
}
internal void WriteRawByte(byte value)
{
if (position == limit)
{
RefreshBuffer();
}
buffer[position++] = value;
}
internal void WriteRawByte(uint value)
{
WriteRawByte((byte) value);
}
//#region Underlying writing primitives
///// <summary>
///// Writes a 32 bit value as a varint. The fast route is taken when
///// there's enough buffer space left to whizz through without checking
///// for each byte; otherwise, we resort to calling WriteRawByte each time.
///// </summary>
//internal void WriteRawVarint32(uint value)
//{
// // Optimize for the common case of a single byte value
// if (value < 128 && position < limit)
// {
// buffer[position++] = (byte)value;
// return;
// }
// while (value > 127 && position < limit)
// {
// buffer[position++] = (byte) ((value & 0x7F) | 0x80);
// value >>= 7;
// }
// while (value > 127)
// {
// WriteRawByte((byte) ((value & 0x7F) | 0x80));
// value >>= 7;
// }
// if (position < limit)
// {
// buffer[position++] = (byte) value;
// }
// else
// {
// WriteRawByte((byte) value);
// }
//}
//internal void WriteRawVarint64(ulong value)
//{
// while (value > 127 && position < limit)
// {
// buffer[position++] = (byte) ((value & 0x7F) | 0x80);
// value >>= 7;
// }
// while (value > 127)
// {
// WriteRawByte((byte) ((value & 0x7F) | 0x80));
// value >>= 7;
// }
// if (position < limit)
// {
// buffer[position++] = (byte) value;
// }
// else
// {
// WriteRawByte((byte) value);
// }
//}
//internal void WriteRawLittleEndian32(uint value)
//{
// if (position + 4 > limit)
// {
// WriteRawByte((byte) value);
// WriteRawByte((byte) (value >> 8));
// WriteRawByte((byte) (value >> 16));
// WriteRawByte((byte) (value >> 24));
// }
// else
// {
// buffer[position++] = ((byte) value);
// buffer[position++] = ((byte) (value >> 8));
// buffer[position++] = ((byte) (value >> 16));
// buffer[position++] = ((byte) (value >> 24));
// }
//}
//internal void WriteRawLittleEndian64(ulong value)
//{
// if (position + 8 > limit)
// {
// WriteRawByte((byte) value);
// WriteRawByte((byte) (value >> 8));
// WriteRawByte((byte) (value >> 16));
// WriteRawByte((byte) (value >> 24));
// WriteRawByte((byte) (value >> 32));
// WriteRawByte((byte) (value >> 40));
// WriteRawByte((byte) (value >> 48));
// WriteRawByte((byte) (value >> 56));
// }
// else
// {
// buffer[position++] = ((byte) value);
// buffer[position++] = ((byte) (value >> 8));
// buffer[position++] = ((byte) (value >> 16));
// buffer[position++] = ((byte) (value >> 24));
// buffer[position++] = ((byte) (value >> 32));
// buffer[position++] = ((byte) (value >> 40));
// buffer[position++] = ((byte) (value >> 48));
// buffer[position++] = ((byte) (value >> 56));
// }
//}
//internal void WriteRawByte(byte value)
//{
// if (position == limit)
// {
// RefreshBuffer();
// }
// buffer[position++] = value;
//}
//internal void WriteRawByte(uint value)
//{
// WriteRawByte((byte) value);
//}
// TODO: get rid of this internal method
/// <summary>
/// Writes out an array of bytes.
/// </summary>
@ -601,89 +573,60 @@ namespace Google.Protobuf
WriteRawBytes(value, 0, value.Length);
}
// TODO: get rid of this internal method
/// <summary>
/// Writes out part of an array of bytes.
/// </summary>
internal void WriteRawBytes(byte[] value, int offset, int length)
{
if (limit - position >= length)
{
ByteArray.Copy(value, offset, buffer, position, length);
// We have room in the current buffer.
position += length;
}
else
{
// Write extends past current buffer. Fill the rest of this buffer and
// flush.
int bytesWritten = limit - position;
ByteArray.Copy(value, offset, buffer, position, bytesWritten);
offset += bytesWritten;
length -= bytesWritten;
position = limit;
RefreshBuffer();
// Now deal with the rest.
// Since we have an output stream, this is our buffer
// and buffer offset == 0
if (length <= limit)
{
// Fits in new buffer.
ByteArray.Copy(value, offset, buffer, 0, length);
position = length;
}
else
{
// Write is very big. Let's do it all at once.
output.Write(value, offset, length);
}
}
}
#endregion
/// <summary>
/// Encode a 32-bit value with ZigZag encoding.
/// </summary>
/// <remarks>
/// ZigZag encodes signed integers into values that can be efficiently
/// encoded with varint. (Otherwise, negative values must be
/// sign-extended to 64 bits to be varint encoded, thus always taking
/// 10 bytes on the wire.)
/// </remarks>
internal static uint EncodeZigZag32(int n)
{
// Note: the right-shift must be arithmetic
return (uint) ((n << 1) ^ (n >> 31));
}
/// <summary>
/// Encode a 64-bit value with ZigZag encoding.
/// </summary>
/// <remarks>
/// ZigZag encodes signed integers into values that can be efficiently
/// encoded with varint. (Otherwise, negative values must be
/// sign-extended to 64 bits to be varint encoded, thus always taking
/// 10 bytes on the wire.)
/// </remarks>
internal static ulong EncodeZigZag64(long n)
{
return (ulong) ((n << 1) ^ (n >> 63));
}
private void RefreshBuffer()
{
if (output == null)
{
// We're writing to a single buffer.
throw new OutOfSpaceException();
}
// Since we have an output stream, this is our buffer
// and buffer offset == 0
output.Write(buffer, 0, position);
position = 0;
}
var span = new Span<byte>(buffer);
WritingPrimitives.WriteRawBytes(ref span, ref state, value, offset, length);
}
//#endregion
///// <summary>
///// Encode a 32-bit value with ZigZag encoding.
///// </summary>
///// <remarks>
///// ZigZag encodes signed integers into values that can be efficiently
///// encoded with varint. (Otherwise, negative values must be
///// sign-extended to 64 bits to be varint encoded, thus always taking
///// 10 bytes on the wire.)
///// </remarks>
//internal static uint EncodeZigZag32(int n)
//{
// // Note: the right-shift must be arithmetic
// return (uint) ((n << 1) ^ (n >> 31));
//}
///// <summary>
///// Encode a 64-bit value with ZigZag encoding.
///// </summary>
///// <remarks>
///// ZigZag encodes signed integers into values that can be efficiently
///// encoded with varint. (Otherwise, negative values must be
///// sign-extended to 64 bits to be varint encoded, thus always taking
///// 10 bytes on the wire.)
///// </remarks>
//internal static ulong EncodeZigZag64(long n)
//{
// return (ulong) ((n << 1) ^ (n >> 63));
//}
//private void RefreshBuffer()
//{
// if (output == null)
// {
// // We're writing to a single buffer.
// throw new OutOfSpaceException();
// }
// // Since we have an output stream, this is our buffer
// // and buffer offset == 0
// output.Write(buffer, 0, position);
// position = 0;
//}
/// <summary>
/// Indicates that a CodedOutputStream wrapping a flat byte array
@ -726,10 +669,13 @@ namespace Google.Protobuf
/// </summary>
public void Flush()
{
if (output != null)
var span = new Span<byte>(buffer);
state.writeBufferHelper.Flush(ref span, ref state);
/*if (output != null)
{
RefreshBuffer();
}
}*/
}
/// <summary>
@ -756,7 +702,7 @@ namespace Google.Protobuf
{
if (output == null)
{
return limit - position;
return state.limit - state.position;
}
else
{
@ -770,5 +716,7 @@ namespace Google.Protobuf
internal byte[] InternalBuffer => buffer;
internal Stream InternalOutputStream => output;
internal ref WriterInternalState InternalState => ref state;
}
}

@ -216,9 +216,9 @@ namespace Google.Protobuf.Collections
if (codec.PackedRepeatedField)
{
// Packed primitive type
uint size = (uint)CalculatePackedDataSize(codec);
int size = CalculatePackedDataSize(codec);
output.WriteTag(tag);
output.WriteRawVarint32(size);
output.WriteLength(size);
for (int i = 0; i < count; i++)
{
writer(output, array[i]);

@ -129,7 +129,7 @@ namespace Google.Protobuf
ProtoPreconditions.CheckNotNull(message, "message");
ProtoPreconditions.CheckNotNull(output, "output");
CodedOutputStream codedOutput = new CodedOutputStream(output);
codedOutput.WriteRawVarint32((uint)message.CalculateSize());
codedOutput.WriteLength(message.CalculateSize());
message.WriteTo(codedOutput);
codedOutput.Flush();
}

Loading…
Cancel
Save