diff --git a/csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs b/csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs index aa7932470b..2dec861483 100644 --- a/csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs +++ b/csharp/src/Google.Protobuf/CodedOutputStream.ComputeSize.cs @@ -208,7 +208,7 @@ namespace Google.Protobuf /// public static int ComputeSInt32Size(int value) { - return ComputeRawVarint32Size(EncodeZigZag32(value)); + return ComputeRawVarint32Size(WritingPrimitives.EncodeZigZag32(value)); } /// @@ -217,7 +217,7 @@ namespace Google.Protobuf /// public static int ComputeSInt64Size(long value) { - return ComputeRawVarint64Size(EncodeZigZag64(value)); + return ComputeRawVarint64Size(WritingPrimitives.EncodeZigZag64(value)); } /// diff --git a/csharp/src/Google.Protobuf/CodedOutputStream.cs b/csharp/src/Google.Protobuf/CodedOutputStream.cs index b348a523c1..ccf7520525 100644 --- a/csharp/src/Google.Protobuf/CodedOutputStream.cs +++ b/csharp/src/Google.Protobuf/CodedOutputStream.cs @@ -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 /// The value to write public void WriteDouble(double value) { - WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value)); + var span = new Span(buffer); + WritingPrimitives.WriteDouble(ref span, ref state, value); } /// @@ -178,23 +181,8 @@ namespace Google.Protobuf /// The value to write 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(buffer); + WritingPrimitives.WriteFloat(ref span, ref state, value); } /// @@ -203,7 +191,8 @@ namespace Google.Protobuf /// The value to write public void WriteUInt64(ulong value) { - WriteRawVarint64(value); + var span = new Span(buffer); + WritingPrimitives.WriteUInt64(ref span, ref state, value); } /// @@ -212,7 +201,8 @@ namespace Google.Protobuf /// The value to write public void WriteInt64(long value) { - WriteRawVarint64((ulong) value); + var span = new Span(buffer); + WritingPrimitives.WriteInt64(ref span, ref state, value); } /// @@ -221,15 +211,8 @@ namespace Google.Protobuf /// The value to write public void WriteInt32(int value) { - if (value >= 0) - { - WriteRawVarint32((uint) value); - } - else - { - // Must sign-extend. - WriteRawVarint64((ulong) value); - } + var span = new Span(buffer); + WritingPrimitives.WriteInt32(ref span, ref state, value); } /// @@ -238,7 +221,8 @@ namespace Google.Protobuf /// The value to write public void WriteFixed64(ulong value) { - WriteRawLittleEndian64(value); + var span = new Span(buffer); + WritingPrimitives.WriteFixed64(ref span, ref state, value); } /// @@ -247,7 +231,8 @@ namespace Google.Protobuf /// The value to write public void WriteFixed32(uint value) { - WriteRawLittleEndian32(value); + var span = new Span(buffer); + WritingPrimitives.WriteFixed32(ref span, ref state, value); } /// @@ -256,7 +241,8 @@ namespace Google.Protobuf /// The value to write public void WriteBool(bool value) { - WriteRawByte(value ? (byte) 1 : (byte) 0); + var span = new Span(buffer); + WritingPrimitives.WriteBool(ref span, ref state, value); } /// @@ -266,30 +252,8 @@ namespace Google.Protobuf /// The value to write 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(buffer); + WritingPrimitives.WriteString(ref span, ref state, value); } /// @@ -299,6 +263,7 @@ namespace Google.Protobuf /// The value to write public void WriteMessage(IMessage value) { + // TOOD: implement.... WriteLength(value.CalculateSize()); value.WriteTo(this); } @@ -309,6 +274,7 @@ namespace Google.Protobuf /// The value to write public void WriteGroup(IMessage value) { + // TODO: implement... value.WriteTo(this); } @@ -319,8 +285,9 @@ namespace Google.Protobuf /// The value to write public void WriteBytes(ByteString value) { - WriteLength(value.Length); - value.WriteRawBytesTo(this); + var span = new Span(buffer); + WritingPrimitives.WriteLength(ref span, ref state, value.Length); + WritingPrimitives.WriteBytes(ref span, ref state, value); } /// @@ -329,7 +296,8 @@ namespace Google.Protobuf /// The value to write public void WriteUInt32(uint value) { - WriteRawVarint32(value); + var span = new Span(buffer); + WritingPrimitives.WriteUInt32(ref span, ref state, value); } /// @@ -338,7 +306,8 @@ namespace Google.Protobuf /// The value to write public void WriteEnum(int value) { - WriteInt32(value); + var span = new Span(buffer); + WritingPrimitives.WriteEnum(ref span, ref state, value); } /// @@ -347,7 +316,8 @@ namespace Google.Protobuf /// The value to write. public void WriteSFixed32(int value) { - WriteRawLittleEndian32((uint) value); + var span = new Span(buffer); + WritingPrimitives.WriteSFixed32(ref span, ref state, value); } /// @@ -356,7 +326,8 @@ namespace Google.Protobuf /// The value to write public void WriteSFixed64(long value) { - WriteRawLittleEndian64((ulong) value); + var span = new Span(buffer); + WritingPrimitives.WriteSFixed64(ref span, ref state, value); } /// @@ -365,7 +336,8 @@ namespace Google.Protobuf /// The value to write public void WriteSInt32(int value) { - WriteRawVarint32(EncodeZigZag32(value)); + var span = new Span(buffer); + WritingPrimitives.WriteSInt32(ref span, ref state, value); } /// @@ -374,7 +346,8 @@ namespace Google.Protobuf /// The value to write public void WriteSInt64(long value) { - WriteRawVarint64(EncodeZigZag64(value)); + var span = new Span(buffer); + WritingPrimitives.WriteSInt64(ref span, ref state, value); } /// @@ -386,7 +359,8 @@ namespace Google.Protobuf /// Length value, in bytes. public void WriteLength(int length) { - WriteRawVarint32((uint) length); + var span = new Span(buffer); + WritingPrimitives.WriteLength(ref span, ref state, length); } #endregion @@ -399,7 +373,8 @@ namespace Google.Protobuf /// The wire format type of the tag to write public void WriteTag(int fieldNumber, WireFormat.WireType type) { - WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type)); + var span = new Span(buffer); + WritingPrimitives.WriteTag(ref span, ref state, fieldNumber, type); } /// @@ -408,7 +383,8 @@ namespace Google.Protobuf /// The encoded tag public void WriteTag(uint tag) { - WriteRawVarint32(tag); + var span = new Span(buffer); + WritingPrimitives.WriteTag(ref span, ref state, tag); } /// @@ -417,7 +393,8 @@ namespace Google.Protobuf /// The encoded tag public void WriteRawTag(byte b1) { - WriteRawByte(b1); + var span = new Span(buffer); + WritingPrimitives.WriteRawTag(ref span, ref state, b1); } /// @@ -427,8 +404,8 @@ namespace Google.Protobuf /// The second byte of the encoded tag public void WriteRawTag(byte b1, byte b2) { - WriteRawByte(b1); - WriteRawByte(b2); + var span = new Span(buffer); + WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2); } /// @@ -439,9 +416,8 @@ namespace Google.Protobuf /// The third byte of the encoded tag public void WriteRawTag(byte b1, byte b2, byte b3) { - WriteRawByte(b1); - WriteRawByte(b2); - WriteRawByte(b3); + var span = new Span(buffer); + WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3); } /// @@ -453,10 +429,8 @@ namespace Google.Protobuf /// The fourth byte of the encoded tag public void WriteRawTag(byte b1, byte b2, byte b3, byte b4) { - WriteRawByte(b1); - WriteRawByte(b2); - WriteRawByte(b3); - WriteRawByte(b4); + var span = new Span(buffer); + WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4); } /// @@ -469,130 +443,128 @@ namespace Google.Protobuf /// The fifth byte of the encoded tag 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(buffer); + WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4, b5); } #endregion - #region Underlying writing primitives - /// - /// 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. - /// - 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 + ///// + ///// 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. + ///// + //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 /// /// Writes out an array of bytes. /// @@ -601,89 +573,60 @@ namespace Google.Protobuf WriteRawBytes(value, 0, value.Length); } + // TODO: get rid of this internal method /// /// Writes out part of an array of bytes. /// 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 - - /// - /// Encode a 32-bit value with ZigZag encoding. - /// - /// - /// 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.) - /// - internal static uint EncodeZigZag32(int n) - { - // Note: the right-shift must be arithmetic - return (uint) ((n << 1) ^ (n >> 31)); - } - - /// - /// Encode a 64-bit value with ZigZag encoding. - /// - /// - /// 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.) - /// - 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(buffer); + WritingPrimitives.WriteRawBytes(ref span, ref state, value, offset, length); + } + + //#endregion + + ///// + ///// Encode a 32-bit value with ZigZag encoding. + ///// + ///// + ///// 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.) + ///// + //internal static uint EncodeZigZag32(int n) + //{ + // // Note: the right-shift must be arithmetic + // return (uint) ((n << 1) ^ (n >> 31)); + //} + + ///// + ///// Encode a 64-bit value with ZigZag encoding. + ///// + ///// + ///// 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.) + ///// + //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; + //} /// /// Indicates that a CodedOutputStream wrapping a flat byte array @@ -726,10 +669,13 @@ namespace Google.Protobuf /// public void Flush() { - if (output != null) + var span = new Span(buffer); + state.writeBufferHelper.Flush(ref span, ref state); + + /*if (output != null) { RefreshBuffer(); - } + }*/ } /// @@ -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; } } diff --git a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs index 5c39aabafb..32f298a79b 100644 --- a/csharp/src/Google.Protobuf/Collections/RepeatedField.cs +++ b/csharp/src/Google.Protobuf/Collections/RepeatedField.cs @@ -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]); diff --git a/csharp/src/Google.Protobuf/MessageExtensions.cs b/csharp/src/Google.Protobuf/MessageExtensions.cs index 51e40915c8..e9a408c9e2 100644 --- a/csharp/src/Google.Protobuf/MessageExtensions.cs +++ b/csharp/src/Google.Protobuf/MessageExtensions.cs @@ -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(); }