Commit 38da52d3 authored by Jon Skeet's avatar Jon Skeet

Micro-optimisations around varints and strings.

parent 272d384f
...@@ -356,14 +356,15 @@ namespace Google.ProtocolBuffers { ...@@ -356,14 +356,15 @@ namespace Google.ProtocolBuffers {
#endregion #endregion
#region Underlying reading primitives #region Underlying reading primitives
/// <summary> /// <summary>
/// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits. /// Same code as ReadRawVarint32, but read each byte individually, checking for
/// buffer overflow.
/// </summary> /// </summary>
/// <returns></returns> private uint SlowReadRawVarint32() {
public uint ReadRawVarint32() {
int tmp = ReadRawByte(); int tmp = ReadRawByte();
if (tmp < 128) { if (tmp < 128) {
return (uint) tmp; return (uint)tmp;
} }
int result = tmp & 0x7f; int result = tmp & 0x7f;
if ((tmp = ReadRawByte()) < 128) { if ((tmp = ReadRawByte()) < 128) {
...@@ -382,14 +383,59 @@ namespace Google.ProtocolBuffers { ...@@ -382,14 +383,59 @@ namespace Google.ProtocolBuffers {
if (tmp >= 128) { if (tmp >= 128) {
// Discard upper 32 bits. // Discard upper 32 bits.
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
if (ReadRawByte() < 128) return (uint) result; if (ReadRawByte() < 128) return (uint)result;
}
throw InvalidProtocolBufferException.MalformedVarint();
}
}
}
}
return (uint)result;
}
/// <summary>
/// Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
/// This method is optimised for the case where we've got lots of data in the buffer.
/// That means we can check the size just once, then just read directly from the buffer
/// without constant rechecking of the buffer length.
/// </summary>
public uint ReadRawVarint32() {
if (bufferPos + 5 > bufferSize) {
return SlowReadRawVarint32();
}
int tmp = buffer[bufferPos++];
if (tmp < 128) {
return (uint)tmp;
}
int result = tmp & 0x7f;
if ((tmp = buffer[bufferPos++]) < 128) {
result |= tmp << 7;
} else {
result |= (tmp & 0x7f) << 7;
if ((tmp = buffer[bufferPos++]) < 128) {
result |= tmp << 14;
} else {
result |= (tmp & 0x7f) << 14;
if ((tmp = buffer[bufferPos++]) < 128) {
result |= tmp << 21;
} else {
result |= (tmp & 0x7f) << 21;
result |= (tmp = buffer[bufferPos++]) << 28;
if (tmp >= 128) {
// Discard upper 32 bits.
// Note that this has to use ReadRawByte() as we only ensure we've
// got at least 5 bytes at the start of the method. This lets us
// use the fast path in more cases, and we rarely hit this section of code.
for (int i = 0; i < 5; i++) {
if (ReadRawByte() < 128) return (uint)result;
} }
throw InvalidProtocolBufferException.MalformedVarint(); throw InvalidProtocolBufferException.MalformedVarint();
} }
} }
} }
} }
return (uint) result; return (uint)result;
} }
/// <summary> /// <summary>
...@@ -571,7 +617,6 @@ namespace Google.ProtocolBuffers { ...@@ -571,7 +617,6 @@ namespace Google.ProtocolBuffers {
bufferPos = 0; bufferPos = 0;
bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length); bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
if (bufferSize == 0) { if (bufferSize == 0) {
bufferSize = 0;
if (mustSucceed) { if (mustSucceed) {
throw InvalidProtocolBufferException.TruncatedMessage(); throw InvalidProtocolBufferException.TruncatedMessage();
} else { } else {
......
...@@ -172,16 +172,18 @@ namespace Google.ProtocolBuffers { ...@@ -172,16 +172,18 @@ namespace Google.ProtocolBuffers {
/// </summary> /// </summary>
public void WriteString(int fieldNumber, string value) { public void WriteString(int fieldNumber, string value) {
WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited); WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
// TODO(jonskeet): Optimise this if possible // Optimise the case where we have enough space to write
// Unfortunately there does not appear to be any way to tell Java to encode // the string directly to the buffer, which should be common.
// UTF-8 directly into our buffer, so we have to let it create its own byte int length = Encoding.UTF8.GetByteCount(value);
// array and then copy. In .NET we can do the same thing very easily, WriteRawVarint32((uint) length);
// so we don't need to worry about only writing one buffer at a time. if (limit - position >= length) {
// We can optimise later. Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position);
position += length;
} else {
byte[] bytes = Encoding.UTF8.GetBytes(value); byte[] bytes = Encoding.UTF8.GetBytes(value);
WriteRawVarint32((uint)bytes.Length);
WriteRawBytes(bytes); WriteRawBytes(bytes);
} }
}
/// <summary> /// <summary>
/// Writes a group field value, including tag, to the stream. /// Writes a group field value, including tag, to the stream.
...@@ -290,7 +292,7 @@ namespace Google.ProtocolBuffers { ...@@ -290,7 +292,7 @@ namespace Google.ProtocolBuffers {
WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type)); WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
} }
public void WriteRawVarint32(uint value) { private void SlowWriteRawVarint32(uint value) {
while (true) { while (true) {
if ((value & ~0x7F) == 0) { if ((value & ~0x7F) == 0) {
WriteRawByte(value); WriteRawByte(value);
...@@ -302,6 +304,28 @@ namespace Google.ProtocolBuffers { ...@@ -302,6 +304,28 @@ namespace Google.ProtocolBuffers {
} }
} }
/// <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>
public void WriteRawVarint32(uint value) {
if (position + 5 > limit) {
SlowWriteRawVarint32(value);
return;
}
while (true) {
if ((value & ~0x7F) == 0) {
buffer[position++] = (byte) value;
return;
} else {
buffer[position++] = (byte)((value & 0x7F) | 0x80);
value >>= 7;
}
}
}
public void WriteRawVarint64(ulong value) { public void WriteRawVarint64(ulong value) {
while (true) { while (true) {
if ((value & ~0x7FUL) == 0) { if ((value & ~0x7FUL) == 0) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment