First part of dotnet library

committer: Jon Skeet <skeet@pobox.com>
This commit is contained in:
Jon Skeet 2008-08-14 20:33:27 +01:00 committed by unknown
parent 3c38991607
commit baa2bf54c2
21 changed files with 6940 additions and 0 deletions

View File

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
namespace Google.ProtocolBuffers {
[TestFixture]
public class ByteStringTest {
[Test]
public void EmptyByteStringHasZeroSize() {
Assert.AreEqual(0, ByteString.Empty.Length);
}
[Test]
public void CopyFromStringWithExplicitEncoding() {
ByteString bs = ByteString.CopyFrom("AB", Encoding.Unicode);
Assert.AreEqual(4, bs.Length);
Assert.AreEqual(65, bs[0]);
Assert.AreEqual(0, bs[1]);
Assert.AreEqual(66, bs[2]);
Assert.AreEqual(0, bs[3]);
}
[Test]
public void IsEmptyWhenEmpty() {
Assert.IsTrue(ByteString.CopyFromUtf8("").IsEmpty);
}
[Test]
public void IsEmptyWhenNotEmpty() {
Assert.IsFalse(ByteString.CopyFromUtf8("X").IsEmpty);
}
[Test]
public void CopyFromByteArrayCopiesContents() {
byte[] data = new byte[1];
data[0] = 10;
ByteString bs = ByteString.CopyFrom(data);
Assert.AreEqual(10, bs[0]);
data[0] = 5;
Assert.AreEqual(10, bs[0]);
}
[Test]
public void ToByteArrayCopiesContents() {
ByteString bs = ByteString.CopyFromUtf8("Hello");
byte[] data = bs.ToByteArray();
Assert.AreEqual('H', data[0]);
Assert.AreEqual('H', bs[0]);
data[0] = 0;
Assert.AreEqual(0, data[0]);
Assert.AreEqual('H', bs[0]);
}
[Test]
public void CopyFromUtf8UsesUtf8() {
ByteString bs = ByteString.CopyFromUtf8("\u20ac");
Assert.AreEqual(3, bs.Length);
Assert.AreEqual(0xe2, bs[0]);
Assert.AreEqual(0x82, bs[1]);
Assert.AreEqual(0xac, bs[2]);
}
[Test]
public void CopyFromPortion() {
byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6};
ByteString bs = ByteString.CopyFrom(data, 2, 3);
Assert.AreEqual(3, bs.Length);
Assert.AreEqual(2, bs[0]);
Assert.AreEqual(3, bs[1]);
}
[Test]
public void ToStringUtf8() {
ByteString bs = ByteString.CopyFromUtf8("\u20ac");
Assert.AreEqual("\u20ac", bs.ToStringUtf8());
}
[Test]
public void ToStringWithExplicitEncoding() {
ByteString bs = ByteString.CopyFrom("\u20ac", Encoding.Unicode);
Assert.AreEqual("\u20ac", bs.ToString(Encoding.Unicode));
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
namespace Google.ProtocolBuffers {
[TestFixture]
public class CodedInputStreamTest {
[Test]
public void DecodeZigZag32() {
Assert.AreEqual( 0, CodedInputStream.DecodeZigZag32(0));
Assert.AreEqual(-1, CodedInputStream.DecodeZigZag32(1));
Assert.AreEqual( 1, CodedInputStream.DecodeZigZag32(2));
Assert.AreEqual(-2, CodedInputStream.DecodeZigZag32(3));
Assert.AreEqual(0x3FFFFFFF, CodedInputStream.DecodeZigZag32(0x7FFFFFFE));
Assert.AreEqual(unchecked((int)0xC0000000), CodedInputStream.DecodeZigZag32(0x7FFFFFFF));
Assert.AreEqual(0x7FFFFFFF, CodedInputStream.DecodeZigZag32(0xFFFFFFFE));
Assert.AreEqual(unchecked((int)0x80000000), CodedInputStream.DecodeZigZag32(0xFFFFFFFF));
}
[Test]
public void DecodeZigZag64() {
Assert.AreEqual( 0, CodedInputStream.DecodeZigZag64(0));
Assert.AreEqual(-1, CodedInputStream.DecodeZigZag64(1));
Assert.AreEqual( 1, CodedInputStream.DecodeZigZag64(2));
Assert.AreEqual(-2, CodedInputStream.DecodeZigZag64(3));
Assert.AreEqual(0x000000003FFFFFFFL, CodedInputStream.DecodeZigZag64(0x000000007FFFFFFEL));
Assert.AreEqual(unchecked((long)0xFFFFFFFFC0000000L), CodedInputStream.DecodeZigZag64(0x000000007FFFFFFFL));
Assert.AreEqual(0x000000007FFFFFFFL, CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFEL));
Assert.AreEqual(unchecked((long)0xFFFFFFFF80000000L), CodedInputStream.DecodeZigZag64(0x00000000FFFFFFFFL));
Assert.AreEqual(0x7FFFFFFFFFFFFFFFL, CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFEL));
Assert.AreEqual(unchecked((long)0x8000000000000000L),CodedInputStream.DecodeZigZag64(0xFFFFFFFFFFFFFFFFL));
}
}
}

View File

@ -0,0 +1,237 @@
using NUnit.Framework;
using System.IO;
namespace Google.ProtocolBuffers {
[TestFixture]
public class CodedOutputStreamTest {
/// <summary>
/// Helper to construct a byte array from a bunch of bytes. The inputs are
/// actually ints so that I can use hex notation and not get stupid errors
/// about precision.
/// </summary>
private static byte[] Bytes(params int[] bytesAsInts) {
byte[] bytes = new byte[bytesAsInts.Length];
for (int i = 0; i < bytesAsInts.Length; i++) {
bytes[i] = (byte) bytesAsInts[i];
}
return bytes;
}
/// <summary>
/// Writes the given value using WriteRawVarint32() and WriteRawVarint64() and
/// checks that the result matches the given bytes
/// </summary>
private static void AssertWriteVarint(byte[] data, ulong value) {
// Only do 32-bit write if the value fits in 32 bits.
if ((value >> 32) == 0) {
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput);
output.WriteRawVarint32((uint) value);
output.Flush();
Assert.AreEqual(data, rawOutput.ToArray());
// Also try computing size.
Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint32Size((uint) value));
}
{
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput);
output.WriteRawVarint64(value);
output.Flush();
Assert.AreEqual(data, rawOutput.ToArray());
// Also try computing size.
Assert.AreEqual(data.Length, CodedOutputStream.ComputeRawVarint64Size(value));
}
// Try different buffer sizes.
for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2) {
// Only do 32-bit write if the value fits in 32 bits.
if ((value >> 32) == 0) {
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output =
CodedOutputStream.CreateInstance(rawOutput, bufferSize);
output.WriteRawVarint32((uint) value);
output.Flush();
Assert.AreEqual(data, rawOutput.ToArray());
}
{
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput, bufferSize);
output.WriteRawVarint64(value);
output.Flush();
Assert.AreEqual(data, rawOutput.ToArray());
}
}
}
/// <summary>
/// Tests WriteRawVarint32() and WriteRawVarint64()
/// </summary>
[Test]
public void WriteVarint() {
AssertWriteVarint(Bytes(0x00), 0);
AssertWriteVarint(Bytes(0x01), 1);
AssertWriteVarint(Bytes(0x7f), 127);
// 14882
AssertWriteVarint(Bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
// 2961488830
AssertWriteVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
(0x0bL << 28));
// 64-bit
// 7256456126
AssertWriteVarint(Bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
(0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
(0x1bL << 28));
// 41256202580718336
AssertWriteVarint(
Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
(0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
(0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
// 11964378330978735131
AssertWriteVarint(
Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
unchecked((ulong)
((0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
(0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
(0x05L << 49) | (0x26L << 56) | (0x01L << 63))));
}
/// <summary>
/// Parses the given bytes using WriteRawLittleEndian32() and checks
/// that the result matches the given value.
/// </summary>
private static void AssertWriteLittleEndian32(byte[] data, int value) {
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput);
output.WriteRawLittleEndian32(value);
output.Flush();
Assert.AreEqual(data, rawOutput.ToArray());
// Try different buffer sizes.
for (int bufferSize = 1; bufferSize <= 16; bufferSize *= 2) {
rawOutput = new MemoryStream();
output = CodedOutputStream.CreateInstance(rawOutput, bufferSize);
output.WriteRawLittleEndian32(value);
output.Flush();
Assert.AreEqual(data, rawOutput.ToArray());
}
}
/// <summary>
/// Parses the given bytes using WriteRawLittleEndian64() and checks
/// that the result matches the given value.
/// </summary>
private static void AssertWriteLittleEndian64(byte[] data, long value) {
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput);
output.WriteRawLittleEndian64(value);
output.Flush();
Assert.AreEqual(data, rawOutput.ToArray());
// Try different block sizes.
for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
rawOutput = new MemoryStream();
output = CodedOutputStream.CreateInstance(rawOutput, blockSize);
output.WriteRawLittleEndian64(value);
output.Flush();
Assert.AreEqual(data, rawOutput.ToArray());
}
}
/// <summary>
/// Tests writeRawLittleEndian32() and writeRawLittleEndian64().
/// </summary>
[Test]
public void WriteLittleEndian() {
AssertWriteLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
AssertWriteLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), unchecked((int)0x9abcdef0));
AssertWriteLittleEndian64(
Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
0x123456789abcdef0L);
AssertWriteLittleEndian64(
Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
unchecked((long)0x9abcdef012345678L));
}
/* TODO(jonskeet): Put this back when we've got the rest working!
[Test]
public void testWriteWholeMessage() throws Exception {
TestAllTypes message = TestUtil.getAllSet();
byte[] rawBytes = message.toByteArray();
assertEqualBytes(TestUtil.getGoldenMessage().toByteArray(), rawBytes);
// Try different block sizes.
for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output =
CodedOutputStream.newInstance(rawOutput, blockSize);
message.writeTo(output);
output.flush();
assertEqualBytes(rawBytes, rawOutput.toByteArray());
}
} */
[Test]
public void EncodeZigZag32() {
Assert.AreEqual(0, CodedOutputStream.EncodeZigZag32( 0));
Assert.AreEqual(1, CodedOutputStream.EncodeZigZag32(-1));
Assert.AreEqual(2, CodedOutputStream.EncodeZigZag32( 1));
Assert.AreEqual(3, CodedOutputStream.EncodeZigZag32(-2));
Assert.AreEqual(0x7FFFFFFE, CodedOutputStream.EncodeZigZag32(0x3FFFFFFF));
Assert.AreEqual(0x7FFFFFFF, CodedOutputStream.EncodeZigZag32(unchecked((int)0xC0000000)));
Assert.AreEqual(0xFFFFFFFE, CodedOutputStream.EncodeZigZag32(0x7FFFFFFF));
Assert.AreEqual(0xFFFFFFFF, CodedOutputStream.EncodeZigZag32(unchecked((int)0x80000000)));
}
[Test]
public void EncodeZigZag64() {
Assert.AreEqual(0, CodedOutputStream.EncodeZigZag64( 0));
Assert.AreEqual(1, CodedOutputStream.EncodeZigZag64(-1));
Assert.AreEqual(2, CodedOutputStream.EncodeZigZag64( 1));
Assert.AreEqual(3, CodedOutputStream.EncodeZigZag64(-2));
Assert.AreEqual(0x000000007FFFFFFEL,
CodedOutputStream.EncodeZigZag64(unchecked((long)0x000000003FFFFFFFUL)));
Assert.AreEqual(0x000000007FFFFFFFL,
CodedOutputStream.EncodeZigZag64(unchecked((long)0xFFFFFFFFC0000000UL)));
Assert.AreEqual(0x00000000FFFFFFFEL,
CodedOutputStream.EncodeZigZag64(unchecked((long)0x000000007FFFFFFFUL)));
Assert.AreEqual(0x00000000FFFFFFFFL,
CodedOutputStream.EncodeZigZag64(unchecked((long)0xFFFFFFFF80000000UL)));
Assert.AreEqual(0xFFFFFFFFFFFFFFFEL,
CodedOutputStream.EncodeZigZag64(unchecked((long)0x7FFFFFFFFFFFFFFFUL)));
Assert.AreEqual(0xFFFFFFFFFFFFFFFFL,
CodedOutputStream.EncodeZigZag64(unchecked((long)0x8000000000000000UL)));
}
[Test]
public void RoundTripZigZag32() {
// Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1)
// were chosen semi-randomly via keyboard bashing.
Assert.AreEqual(0, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(0)));
Assert.AreEqual(1, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(1)));
Assert.AreEqual(-1, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-1)));
Assert.AreEqual(14927, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(14927)));
Assert.AreEqual(-3612, CodedInputStream.DecodeZigZag32(CodedOutputStream.EncodeZigZag32(-3612)));
}
[Test]
public void RoundTripZigZag64() {
Assert.AreEqual(0, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(0)));
Assert.AreEqual(1, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(1)));
Assert.AreEqual(-1, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-1)));
Assert.AreEqual(14927, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(14927)));
Assert.AreEqual(-3612, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-3612)));
Assert.AreEqual(856912304801416L, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(856912304801416L)));
Assert.AreEqual(-75123905439571256L, CodedInputStream.DecodeZigZag64(CodedOutputStream.EncodeZigZag64(-75123905439571256L)));
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ProtocolBuffers.Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ProtocolBuffers.Test")]
[assembly: AssemblyCopyright("Copyright © 2008")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("54e627c3-daaa-4850-82cf-f25b7f097e78")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{DD01ED24-3750-4567-9A23-1DB676A15610}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Google.ProtocolBuffers</RootNamespace>
<AssemblyName>Google.ProtocolBuffers.Test</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="nunit.framework, Version=2.2.8.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77">
<SpecificVersion>False</SpecificVersion>
<HintPath>lib\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="Rhino.Mocks, Version=3.5.0.2, Culture=neutral, PublicKeyToken=0b3305902db7183f, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>lib\Rhino.Mocks.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ByteStringTest.cs" />
<Compile Include="CodedInputStreamTest.cs" />
<Compile Include="CodedOutputStreamTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ProtocolBuffers\ProtocolBuffers.csproj">
<Project>{6908BDCE-D925-43F3-94AC-A531E6DF2591}</Project>
<Name>ProtocolBuffers</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,96 @@
using System.Text;
using System;
namespace Google.ProtocolBuffers {
/// <summary>
/// Immutable array of bytes.
/// TODO(jonskeet): Implement the common collection interfaces?
/// </summary>
public sealed class ByteString {
private static readonly ByteString empty = new ByteString(new byte[0]);
private byte[] bytes;
/// <summary>
/// Constructs a new ByteString from the given byte array. The array is
/// *not* copied, and must not be modified after this constructor is called.
/// </summary>
private ByteString(byte[] bytes) {
this.bytes = bytes;
}
/// <summary>
/// Returns an empty ByteString.
/// </summary>
public static ByteString Empty {
get { return empty; }
}
/// <summary>
/// Returns the length of this ByteString in bytes.
/// </summary>
public int Length {
get { return bytes.Length; }
}
public bool IsEmpty {
get { return Length == 0; }
}
public byte[] ToByteArray() {
return (byte[])bytes.Clone();
}
/// <summary>
/// Constructs a ByteString from the given array. The contents
/// are copied, so further modifications to the array will not
/// be reflected in the returned ByteString.
/// </summary>
public static ByteString CopyFrom(byte[] bytes) {
return new ByteString((byte[]) bytes.Clone());
}
/// <summary>
/// Constructs a ByteString from a portion of a byte array.
/// </summary>
public static ByteString CopyFrom(byte[] bytes, int offset, int count) {
byte[] portion = new byte[count];
Array.Copy(bytes, offset, portion, 0, count);
return new ByteString(portion);
}
/// <summary>
/// Creates a new ByteString by encoding the specified text with
/// the given encoding.
/// </summary>
public static ByteString CopyFrom(string text, Encoding encoding) {
return new ByteString(encoding.GetBytes(text));
}
/// <summary>
/// Creates a new ByteString by encoding the specified text in UTF-8.
/// </summary>
public static ByteString CopyFromUtf8(string text) {
return CopyFrom(text, Encoding.UTF8);
}
/// <summary>
/// Retuns the byte at the given index.
/// </summary>
public byte this[int index] {
get { return bytes[index]; }
}
public string ToString(Encoding encoding) {
return encoding.GetString(bytes);
}
public string ToStringUtf8() {
return ToString(Encoding.UTF8);
}
// TODO(jonskeet): CopyTo, Equals, GetHashCode if they turn out to be required
// TODO(jonskeet): Input/Output stuff
}
}

View File

@ -0,0 +1,31 @@

namespace Google.ProtocolBuffers {
public sealed class CodedInputStream {
/// <summary>
/// Decode 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>
public static int DecodeZigZag32(uint n) {
return (int)(n >> 1) ^ -(int)(n & 1);
}
/// <summary>
/// Decode 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>
public static long DecodeZigZag64(ulong n) {
return (long)(n >> 1) ^ -(long)(n & 1);
}
}
}

View File

@ -0,0 +1,708 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.
// http://code.google.com/p/protobuf/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.IO;
using System.Text;
namespace Google.ProtocolBuffers {
/// <summary>
/// Encodes and writes protocol message fields.
/// </summary>
/// <remarks>
/// This class contains two kinds of methods: methods that write specific
/// protocol message constructs and field types (e.g. WriteTag and
/// WriteInt32) and methods that write low-level values (e.g.
/// WriteRawVarint32 and WriteRawBytes). If you are writing encoded protocol
/// messages, you should use the former methods, but if you are writing some
/// other format of your own design, use the latter. The names of the former
/// methods are taken from the protocol buffer type names, not .NET types.
/// (Hence WriteFloat instead of WriteSingle, and WriteBool instead of WriteBoolean.)
/// </remarks>
public sealed class CodedOutputStream {
/// <summary>
/// The buffer size used by CreateInstance(Stream).
/// </summary>
public static readonly int DefaultBufferSize = 4096;
private readonly byte[] buffer;
private readonly int limit;
private int position;
private readonly Stream output;
#region Construction
private CodedOutputStream(byte[] buffer, int offset, int length) {
this.output = null;
this.buffer = buffer;
this.position = offset;
this.limit = offset + length;
}
private CodedOutputStream(Stream output, byte[] buffer) {
this.output = output;
this.buffer = buffer;
this.position = 0;
this.limit = buffer.Length;
}
/// <summary>
/// Creates a new CodedOutputStream which write to the given stream.
/// </summary>
public static CodedOutputStream CreateInstance(Stream output) {
return CreateInstance(output, DefaultBufferSize);
}
/// <summary>
/// Creates a new CodedOutputStream which write to the given stream and uses
/// the specified buffer size.
/// </summary>
public static CodedOutputStream CreateInstance(Stream output, int bufferSize) {
return new CodedOutputStream(output, new byte[bufferSize]);
}
/// <summary>
/// Creates a new CodedOutputStream that writes directly to the given
/// byte array. If more bytes are written than fit in the array,
/// OutOfSpaceException will be thrown.
/// </summary>
public static CodedOutputStream CreateInstance(byte[] flatArray) {
return CreateInstance(flatArray, 0, flatArray.Length);
}
/// <summary>
/// Creates a new CodedOutputStream that writes directly to the given
/// byte array slice. If more bytes are written than fit in the array,
/// OutOfSpaceException will be thrown.
/// </summary>
public static CodedOutputStream CreateInstance(byte[] flatArray, int offset, int length) {
return new CodedOutputStream(flatArray, offset, length);
}
#endregion
#region Writing of tags etc
/// <summary>
/// Writes a double field value, including tag, to the stream.
/// </summary>
public void WriteDouble(int fieldNumber, double value) {
WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
WriteRawLittleEndian64(BitConverter.DoubleToInt64Bits(value));
}
/// <summary>
/// Writes a float field value, including tag, to the stream.
/// </summary>
public void WriteFloat(int fieldNumber, float value) {
WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
// FIXME: How do we convert a single to 32 bits? (Without unsafe code)
//WriteRawLittleEndian32(BitConverter.SingleT(value));
}
/// <summary>
/// Writes a uint64 field value, including tag, to the stream.
/// </summary>
public void WriteUInt64(int fieldNumber, ulong value) {
WriteTag(fieldNumber, WireFormat.WireType.Varint);
WriteRawVarint64(value);
}
/// <summary>
/// Writes an int64 field value, including tag, to the stream.
/// </summary>
public void WriteInt64(int fieldNumber, long value) {
WriteTag(fieldNumber, WireFormat.WireType.Varint);
WriteRawVarint64((ulong)value);
}
/// <summary>
/// Writes an int32 field value, including tag, to the stream.
/// </summary>
public void WriteInt32(int fieldNumber, int value) {
WriteTag(fieldNumber, WireFormat.WireType.Varint);
if (value >= 0) {
WriteRawVarint32((uint)value);
} else {
// Must sign-extend.
WriteRawVarint64((ulong)value);
}
}
/// <summary>
/// Writes a fixed64 field value, including tag, to the stream.
/// </summary>
public void WriteFixed64(int fieldNumber, long value) {
WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
WriteRawLittleEndian64(value);
}
/// <summary>
/// Writes a fixed32 field value, including tag, to the stream.
/// </summary>
public void WriteFixed32(int fieldNumber, int value) {
WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
WriteRawLittleEndian32(value);
}
/// <summary>
/// Writes a bool field value, including tag, to the stream.
/// </summary>
public void WriteBool(int fieldNumber, bool value) {
WriteTag(fieldNumber, WireFormat.WireType.Varint);
WriteRawByte(value ? (byte)1 : (byte)0);
}
/// <summary>
/// Writes a string field value, including tag, to the stream.
/// </summary>
public void WriteString(int fieldNumber, string value) {
WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
// TODO(jonskeet): Optimise this if possible
// Unfortunately there does not appear to be any way to tell Java to encode
// UTF-8 directly into our buffer, so we have to let it create its own byte
// array and then copy. In .NET we can do the same thing very easily,
// so we don't need to worry about only writing one buffer at a time.
// We can optimise later.
byte[] bytes = Encoding.UTF8.GetBytes(value);
WriteRawVarint32((uint)bytes.Length);
WriteRawBytes(bytes);
}
/// <summary>
/// Writes a group field value, including tag, to the stream.
/// </summary>
public void WriteGroup(int fieldNumber, IMessage value) {
WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
value.WriteTo(this);
WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
}
public void WriteUnknownGroup(int fieldNumber, UnknownFieldSet value) {
WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
value.WriteTo(this);
WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
}
public void WriteMessage(int fieldNumber, IMessage value) {
WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
WriteRawVarint32((uint)value.SerializedSize);
value.WriteTo(this);
}
public void WriteBytes(int fieldNumber, ByteString value) {
// TODO(jonskeet): Optimise this! (No need to copy the bytes twice.)
byte[] bytes = value.ToByteArray();
WriteRawVarint32((uint)bytes.Length);
WriteRawBytes(bytes);
}
public void WriteUInt32(int fieldNumber, uint value) {
WriteTag(fieldNumber, WireFormat.WireType.Varint);
WriteRawVarint32(value);
}
public void WriteEnum(int fieldNumber, int value) {
WriteTag(fieldNumber, WireFormat.WireType.Varint);
WriteRawVarint32((uint)value);
}
public void WriteSFixed32(int fieldNumber, int value) {
WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
WriteRawVarint32((uint)value);
}
public void WriteSFixed64(int fieldNumber, long value) {
WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
WriteRawVarint64((ulong)value);
}
public void WriteSInt32(int fieldNumber, int value) {
WriteTag(fieldNumber, WireFormat.WireType.Varint);
WriteRawVarint32(EncodeZigZag32(value));
}
public void WriteSInt64(int fieldNumber, long value) {
WriteTag(fieldNumber, WireFormat.WireType.Varint);
WriteRawVarint64(EncodeZigZag64(value));
}
public void WriteMessageSetExtension(int fieldNumber, IMessage value) {
WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
WriteUInt32(WireFormat.MessageSetField.TypeID, (uint)fieldNumber);
WriteMessage(WireFormat.MessageSetField.Message, value);
WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
}
public void WriteRawMessageSetExtension(int fieldNumber, ByteString value) {
WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.StartGroup);
WriteUInt32(WireFormat.MessageSetField.TypeID, (uint)fieldNumber);
WriteBytes(WireFormat.MessageSetField.Message, value);
WriteTag(WireFormat.MessageSetField.Item, WireFormat.WireType.EndGroup);
}
public void WriteField(Descriptors.FieldDescriptor.Type fieldType, int fieldNumber, object value) {
switch (fieldType) {
case Descriptors.FieldDescriptor.Type.Double: WriteDouble(fieldNumber, (double)value); break;
case Descriptors.FieldDescriptor.Type.Float: WriteFloat(fieldNumber, (float)value); break;
case Descriptors.FieldDescriptor.Type.Int64: WriteInt64(fieldNumber, (long)value); break;
case Descriptors.FieldDescriptor.Type.UInt64: WriteUInt64(fieldNumber, (ulong)value); break;
case Descriptors.FieldDescriptor.Type.Int32: WriteInt32(fieldNumber, (int)value); break;
case Descriptors.FieldDescriptor.Type.Fixed64: WriteFixed64(fieldNumber, (long)value); break;
case Descriptors.FieldDescriptor.Type.Fixed32: WriteFixed32(fieldNumber, (int)value); break;
case Descriptors.FieldDescriptor.Type.Bool: WriteBool(fieldNumber, (bool)value); break;
case Descriptors.FieldDescriptor.Type.String: WriteString(fieldNumber, (string)value); break;
case Descriptors.FieldDescriptor.Type.Group: WriteGroup(fieldNumber, (IMessage)value); break;
case Descriptors.FieldDescriptor.Type.Message: WriteMessage(fieldNumber, (IMessage)value); break;
case Descriptors.FieldDescriptor.Type.Bytes: WriteBytes(fieldNumber, (ByteString)value); break;
case Descriptors.FieldDescriptor.Type.UInt32: WriteUInt32(fieldNumber, (uint)value); break;
case Descriptors.FieldDescriptor.Type.SFixed32: WriteSFixed32(fieldNumber, (int)value); break;
case Descriptors.FieldDescriptor.Type.SFixed64: WriteSFixed64(fieldNumber, (long)value); break;
case Descriptors.FieldDescriptor.Type.SInt32: WriteSInt32(fieldNumber, (int)value); break;
case Descriptors.FieldDescriptor.Type.SInt64: WriteSInt64(fieldNumber, (long)value); break;
case Descriptors.FieldDescriptor.Type.Enum: WriteEnum(fieldNumber, ((Descriptors.EnumValueDescriptor)value).Number);
break;
}
}
#endregion
#region Underlying writing primitives
/// <summary>
/// Encodes and writes a tag.
/// </summary>
public void WriteTag(int fieldNumber, WireFormat.WireType type) {
WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
}
public void WriteRawVarint32(uint value) {
while (true) {
if ((value & ~0x7F) == 0) {
WriteRawByte(value);
return;
} else {
WriteRawByte((value & 0x7F) | 0x80);
value >>= 7;
}
}
}
public void WriteRawVarint64(ulong value) {
while (true) {
if ((value & ~0x7FUL) == 0) {
WriteRawByte((uint)value);
return;
} else {
WriteRawByte(((uint)value & 0x7F) | 0x80);
value >>= 7;
}
}
}
public void WriteRawLittleEndian32(int value) {
WriteRawByte((byte)value);
WriteRawByte((byte)(value >> 8));
WriteRawByte((byte)(value >> 16));
WriteRawByte((byte)(value >> 24));
}
public void WriteRawLittleEndian64(long value) {
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));
}
public void WriteRawByte(byte value) {
if (position == limit) {
RefreshBuffer();
}
buffer[position++] = value;
}
public void WriteRawByte(uint value) {
WriteRawByte((byte)value);
}
/// <summary>
/// Writes out an array of bytes.
/// </summary>
public void WriteRawBytes(byte[] value) {
WriteRawBytes(value, 0, value.Length);
}
/// <summary>
/// Writes out part of an array of bytes.
/// </summary>
public void WriteRawBytes(byte[] value, int offset, int length) {
if (limit - position >= length) {
Array.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;
Array.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.
Array.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
#region Size computations
const int LittleEndian64Size = 8;
const int LittleEndian32Size = 4;
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// double field, including the tag.
/// </summary>
public static int ComputeDoubleSize(int fieldNumber, double value) {
return ComputeTagSize(fieldNumber) + LittleEndian64Size;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// float field, including the tag.
/// </summary>
public static int ComputeFloatSize(int fieldNumber, float value) {
return ComputeTagSize(fieldNumber) + LittleEndian32Size;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// uint64 field, including the tag.
/// </summary>
public static int ComputeUInt64Size(int fieldNumber, ulong value) {
return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(value);
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// int64 field, including the tag.
/// </summary>
public static int ComputeInt64Size(int fieldNumber, long value) {
return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size((ulong)value);
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// int32 field, including the tag.
/// </summary>
public static int ComputeInt32Size(int fieldNumber, int value) {
if (value >= 0) {
return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
} else {
// Must sign-extend.
return ComputeTagSize(fieldNumber) + 10;
}
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// fixed64 field, including the tag.
/// </summary>
public static int ComputeFixed64Size(int fieldNumber, long value) {
return ComputeTagSize(fieldNumber) + LittleEndian64Size;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// fixed32 field, including the tag.
/// </summary>
public static int ComputeFixed32Size(int fieldNumber, int value) {
return ComputeTagSize(fieldNumber) + LittleEndian32Size;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// bool field, including the tag.
/// </summary>
public static int ComputeBoolSize(int fieldNumber, bool value) {
return ComputeTagSize(fieldNumber) + 1;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// string field, including the tag.
/// </summary>
public static int ComputeStringSize(int fieldNumber, String value) {
int byteArraySize = Encoding.UTF8.GetByteCount(value);
return ComputeTagSize(fieldNumber) +
ComputeRawVarint32Size((uint)byteArraySize) +
byteArraySize;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// group field, including the tag.
/// </summary>
public static int ComputeGroupSize(int fieldNumber, IMessage value) {
return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// group field represented by an UnknownFieldSet, including the tag.
/// </summary>
public static int ComputeUnknownGroupSize(int fieldNumber,
UnknownFieldSet value) {
return ComputeTagSize(fieldNumber) * 2 + value.SerializedSize;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// embedded message field, including the tag.
/// </summary>
public static int ComputeMessageSize(int fieldNumber, IMessage value) {
int size = value.SerializedSize;
return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)size) + size;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// bytes field, including the tag.
/// </summary>
public static int ComputeBytesSize(int fieldNumber, ByteString value) {
return ComputeTagSize(fieldNumber) +
ComputeRawVarint32Size((uint)value.Length) +
value.Length;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// uint32 field, including the tag.
/// </summary>
public static int ComputeUInt32Size(int fieldNumber, uint value) {
return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(value);
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// enum field, including the tag. The caller is responsible for
/// converting the enum value to its numeric value.
/// </summary>
public static int ComputeEnumSize(int fieldNumber, int value) {
return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size((uint)value);
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// sfixed32 field, including the tag.
/// </summary>
public static int ComputeSFixed32Size(int fieldNumber, int value) {
return ComputeTagSize(fieldNumber) + LittleEndian32Size;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// sfixed64 field, including the tag.
/// </summary>
public static int ComputeSFixed64Size(int fieldNumber, long value) {
return ComputeTagSize(fieldNumber) + LittleEndian64Size;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// sint32 field, including the tag.
/// </summary>
public static int ComputeSInt32Size(int fieldNumber, int value) {
return ComputeTagSize(fieldNumber) +
ComputeRawVarint32Size(EncodeZigZag32(value));
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// sint64 field, including the tag.
/// </summary>
public static int ComputeSInt64Size(int fieldNumber, long value) {
return ComputeTagSize(fieldNumber) +
ComputeRawVarint64Size(EncodeZigZag64(value));
}
/*
* Compute the number of bytes that would be needed to encode a
* MessageSet extension to the stream. For historical reasons,
* the wire format differs from normal fields.
*/
/// <summary>
/// Compute the number of bytes that would be needed to encode a
/// MessageSet extension to the stream. For historical reasons,
/// the wire format differs from normal fields.
/// </summary>
public static int ComputeMessageSetExtensionSize(int fieldNumber, IMessage value) {
return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
ComputeMessageSize(WireFormat.MessageSetField.Message, value);
}
/// <summary>
/// Compute the number of bytes that would be needed to encode an
/// unparsed MessageSet extension field to the stream. For
/// historical reasons, the wire format differs from normal fields.
/// </summary>
public static int ComputeRawMessageSetExtensionSize(int fieldNumber, ByteString value) {
return ComputeTagSize(WireFormat.MessageSetField.Item) * 2 +
ComputeUInt32Size(WireFormat.MessageSetField.TypeID, (uint) fieldNumber) +
ComputeBytesSize(WireFormat.MessageSetField.Message, value);
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a varint.
/// </summary>
public static int ComputeRawVarint32Size(uint value) {
if ((value & (0xffffffff << 7)) == 0) return 1;
if ((value & (0xffffffff << 14)) == 0) return 2;
if ((value & (0xffffffff << 21)) == 0) return 3;
if ((value & (0xffffffff << 28)) == 0) return 4;
return 5;
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a varint.
/// </summary>
public static int ComputeRawVarint64Size(ulong value) {
if ((value & (0xffffffffffffffffL << 7)) == 0) return 1;
if ((value & (0xffffffffffffffffL << 14)) == 0) return 2;
if ((value & (0xffffffffffffffffL << 21)) == 0) return 3;
if ((value & (0xffffffffffffffffL << 28)) == 0) return 4;
if ((value & (0xffffffffffffffffL << 35)) == 0) return 5;
if ((value & (0xffffffffffffffffL << 42)) == 0) return 6;
if ((value & (0xffffffffffffffffL << 49)) == 0) return 7;
if ((value & (0xffffffffffffffffL << 56)) == 0) return 8;
if ((value & (0xffffffffffffffffL << 63)) == 0) return 9;
return 10;
}
/*
* Compute the number of bytes that would be needed to encode a
* field of arbitrary type, including tag, to the stream.
*
* @param type The field's type.
* @param number The field's number.
* @param value Object representing the field's value. Must be of the exact
* type which would be returned by
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
public static int ComputeFieldSize(Descriptors.FieldDescriptor.Type fieldType, int fieldNumber, Object value) {
switch (fieldType) {
case Descriptors.FieldDescriptor.Type.Double: return ComputeDoubleSize(fieldNumber, (double)value);
case Descriptors.FieldDescriptor.Type.Float: return ComputeFloatSize(fieldNumber, (float)value);
case Descriptors.FieldDescriptor.Type.Int64: return ComputeInt64Size(fieldNumber, (long)value);
case Descriptors.FieldDescriptor.Type.UInt64: return ComputeUInt64Size(fieldNumber, (ulong)value);
case Descriptors.FieldDescriptor.Type.Int32: return ComputeInt32Size(fieldNumber, (int)value);
case Descriptors.FieldDescriptor.Type.Fixed64: return ComputeFixed64Size(fieldNumber, (long)value);
case Descriptors.FieldDescriptor.Type.Fixed32: return ComputeFixed32Size(fieldNumber, (int)value);
case Descriptors.FieldDescriptor.Type.Bool: return ComputeBoolSize(fieldNumber, (bool)value);
case Descriptors.FieldDescriptor.Type.String: return ComputeStringSize(fieldNumber, (string)value);
case Descriptors.FieldDescriptor.Type.Group: return ComputeGroupSize(fieldNumber, (IMessage)value);
case Descriptors.FieldDescriptor.Type.Message: return ComputeMessageSize(fieldNumber, (IMessage)value);
case Descriptors.FieldDescriptor.Type.Bytes: return ComputeBytesSize(fieldNumber, (ByteString)value);
case Descriptors.FieldDescriptor.Type.UInt32: return ComputeUInt32Size(fieldNumber, (uint)value);
case Descriptors.FieldDescriptor.Type.SFixed32: return ComputeSFixed32Size(fieldNumber, (int)value);
case Descriptors.FieldDescriptor.Type.SFixed64: return ComputeSFixed64Size(fieldNumber, (long)value);
case Descriptors.FieldDescriptor.Type.SInt32: return ComputeSInt32Size(fieldNumber, (int)value);
case Descriptors.FieldDescriptor.Type.SInt64: return ComputeSInt64Size(fieldNumber, (long)value);
case Descriptors.FieldDescriptor.Type.Enum: return ComputeEnumSize(fieldNumber, ((Descriptors.EnumValueDescriptor)value).Number);
default:
throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
}
}
/// <summary>
/// Compute the number of bytes that would be needed to encode a tag.
/// </summary>
public static int ComputeTagSize(int fieldNumber) {
return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
}
#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>
public 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>
public 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
/// ran out of space.
/// </summary>
public class OutOfSpaceException : IOException {
internal OutOfSpaceException()
: base("CodedOutputStream was writing to a flat byte array and ran out of space.") {
}
}
public void Flush() {
if (output != null) {
RefreshBuffer();
}
}
}
}

View File

@ -0,0 +1,39 @@

using System;
namespace Google.ProtocolBuffers {
public class Descriptors {
public class Descriptor {
}
public class FieldDescriptor {
public enum Type {
Double,
Float,
Int64,
UInt64,
Int32,
Fixed64,
Fixed32,
Bool,
String,
Group,
Message,
Bytes,
UInt32,
SFixed32,
SFixed64,
SInt32,
SInt64,
Enum
}
}
public class EnumValueDescriptor
{
public int Number
{
get { throw new NotImplementedException(); }
}
}
}
}

View File

@ -0,0 +1,5 @@

namespace Google.ProtocolBuffers {
public class ExtensionRegistry {
}
}

View File

@ -0,0 +1,227 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace Google.ProtocolBuffers {
/// <summary>
/// Interface implemented by Protocol Message builders.
/// TODO(jonskeet): Consider "SetXXX" methods returning the builder, as well as the properties.
/// </summary>
/// <typeparam name="T">Type of message</typeparam>
public interface IBuilder<T> where T : IMessage<T> {
/// <summary>
/// Resets all fields to their default values.
/// </summary>
IBuilder<T> Clear();
/// <summary>
/// Merge the specified other message into the message being
/// built. Merging occurs as follows. For each field:
/// For singular primitive fields, if the field is set in <paramref name="other"/>,
/// then <paramref name="other"/>'s value overwrites the value in this message.
/// For singular message fields, if the field is set in <paramref name="other"/>,
/// it is merged into the corresponding sub-message of this message using the same
/// merging rules.
/// For repeated fields, the elements in <paramref name="other"/> are concatenated
/// with the elements in this message.
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
IBuilder<T> MergeFrom(IMessage<T> other);
/// <summary>
/// Constructs the final message. Once this is called, this Builder instance
/// is no longer valid, and calling any other method may throw a
/// NullReferenceException. If you need to continue working with the builder
/// after calling Build, call Clone first.
/// </summary>
/// <exception cref="UninitializedMessageException">the message
/// is missing one or more required fields; use BuildPartial to bypass
/// this check</exception>
IMessage<T> Build();
/// <summary>
/// Like Build(), but does not throw an exception if the message is missing
/// required fields. Instead, a partial message is returned.
/// </summary>
/// <returns></returns>
IMessage<T> BuildPartial();
/// <summary>
/// Clones this builder.
/// TODO(jonskeet): Explain depth of clone.
/// </summary>
IBuilder<T> Clone();
/// <summary>
/// Returns true iff all required fields in the message and all
/// embedded messages are set.
/// </summary>
bool Initialized { get; }
/// <summary>
/// Parses a message of this type from the input and merges it with this
/// message, as if using MergeFrom(IMessage&lt;T&gt;).
/// </summary>
/// <remarks>
/// Warning: This does not verify that all required fields are present
/// in the input message. If you call Build() without setting all
/// required fields, it will throw an UninitializedMessageException.
/// There are a few good ways to deal with this:
/// <list>
/// <item>Call Initialized to verify to verify that all required fields are
/// set before building.</item>
/// <item>Parse the message separately using one of the static ParseFrom
/// methods, then use MergeFrom(IMessage&lt;T&gt;) to merge it with
/// this one. ParseFrom will throw an InvalidProtocolBufferException
/// (an IOException) if some required fields are missing.
/// Use BuildPartial to build, which ignores missing required fields.
/// </list>
/// </remarks>
IBuilder<T> MergeFrom(CodedInputStream input);
/// <summary>
/// Like MergeFrom(CodedInputStream), but also parses extensions.
/// The extensions that you want to be able to parse must be registered
/// in <paramref name="extensionRegistry"/>. Extensions not in the registry
/// will be treated as unknown fields.
/// </summary>
IBuilder<T> MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry);
/// <summary>
/// Get the message's type's descriptor.
/// <see cref="IMessage{T}.DescriptorForType"/>
/// </summary>
Descriptors.Descriptor DescriptorForType { get; }
/// <summary>
/// Get's the message's type's default instance.
/// <see cref="IMessage{T}.DefaultInstanceForType" />
/// </summary>
IMessage<T> DefaultInstanceForType { get; }
/// <summary>
/// Behaves like the equivalent property in IMessage&lt;T&gt;.
/// The returned map may or may not reflect future changes to the builder.
/// Either way, the returned map is unmodifiable.
/// </summary>
IDictionary<ProtocolBuffers.Descriptors.FieldDescriptor, object> AllFields { get; }
/// <summary>
/// Create a builder for messages of the appropriate type for the given field.
/// Messages built with this can then be passed to the various mutation properties
/// and methods.
/// </summary>
/// <typeparam name="TField"></typeparam>
/// <param name="field"></param>
/// <returns></returns>
IBuilder<TField> NewBuilderForField<TField>(Descriptors.FieldDescriptor field)
where TField : IMessage<TField>;
/// <summary>
/// <see cref="IMessage{T}.HasField"/>
/// </summary>
bool HasField(Descriptors.FieldDescriptor field);
/// <summary>
/// Allows getting and setting of a field.
/// <see cref="IMessage{T}.Item(Descriptors.FieldDescriptor)"/>
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
object this[Descriptors.FieldDescriptor field] { get; set; }
/// <summary>
/// Clears the field. This is exactly equivalent to calling the generated
/// Clear method corresponding to the field.
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
IBuilder<T> ClearField(Descriptors.FieldDescriptor field);
/// <summary>
/// <see cref="IMessage{T}.GetRepeatedFieldCount"/>
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
int GetRepeatedFieldCount(Descriptors.FieldDescriptor field);
/// <summary>
/// Allows getting and setting of a repeated field value.
/// <see cref="IMessage{T}.Item(Descriptors.FieldDescriptor, int)"/>
/// </summary>
object this[Descriptors.FieldDescriptor field, int index] { get; set; }
/// <summary>
/// Appends the given value as a new element for the specified repeated field.
/// </summary>
/// <exception cref="ArgumentException">the field is not a repeated field,
/// the field does not belong to this builder's type, or the value is
/// of the incorrect type
/// </exception>
IBuilder<T> AddRepeatedField(Descriptors.FieldDescriptor field, object value);
/// <summary>
/// <see cref="IMessage{T}.UnknownFields"/>
/// </summary>
UnknownFieldSet UnknownFields { get; set; }
/// <summary>
/// Merge some unknown fields into the set for this message.
/// </summary>
IBuilder<T> MergeUnknownFields(UnknownFieldSet unknownFields);
#region Convenience methods
// TODO(jonskeet): Implement these as extension methods?
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream).
/// </summary>
IBuilder<T> MergeFrom(ByteString data);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream, ExtensionRegistry).
/// </summary>
IBuilder<T> MergeFrom(ByteString data, ExtensionRegistry extensionRegistry);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream).
/// </summary>
IBuilder<T> MergeFrom(byte[] data);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream, ExtensionRegistry).
/// </summary>
IBuilder<T> MergeFrom(byte[] data, ExtensionRegistry extensionRegistry);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream). Note that this method always reads
/// the entire input (unless it throws an exception). If you want it to
/// stop earlier, you will need to wrap the input in a wrapper
/// stream which limits reading. Despite usually reading the entire
/// stream, this method never closes the stream.
/// </summary>
IBuilder<T> MergeFrom(Stream input);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream, ExtensionRegistry).
/// </summary>
IBuilder<T> MergeFrom(Stream input, ExtensionRegistry extensionRegistry);
#endregion
}
}

View File

@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Google.ProtocolBuffers {
/// <summary>
/// Non-generic interface.
/// TODO(jonskeet): Do we want or need this?
/// </summary>
public interface IMessage {
void WriteTo(CodedOutputStream output);
int SerializedSize { get; }
}
/// <summary>
/// Interface implemented by all Protocol Buffers messages.
/// </summary>
public interface IMessage<T> where T : IMessage<T> {
/// <summary>
/// Returns the message's type's descriptor. This differs from the
/// Descriptor property of each generated message class in that this
/// method is an abstract method of IMessage whereas Descriptor is
/// a static property of a specific class. They return the same thing.
/// </summary>
Descriptors.Descriptor DescriptorForType { get; }
/// <summary>
/// Returns an instance of this message type with all fields set to
/// their default values. This may or may not be a singleton. This differs
/// from the DefaultInstance property of each generated message class in that this
/// method is an abstract method of IMessage whereas DefaultInstance is
/// a static property of a specific class. They return the same thing.
/// </summary>
IMessage<T> DefaultInstanceForType { get; }
/// <summary>
/// Returns a collection of all the fields in this message which are set
/// and their corresponding values. A singular ("required" or "optional")
/// field is set iff HasField() returns true for that field. A "repeated"
/// field is set iff GetRepeatedFieldSize() is greater than zero. The
/// values are exactly what would be returned by calling
/// GetField(Descriptors.FieldDescriptor) for each field. The map
/// is guaranteed to be a sorted map, so iterating over it will return fields
/// in order by field number.
/// </summary>
IDictionary<Descriptors.FieldDescriptor, object> AllFields { get; }
/// <summary>
/// Returns true if the given field is set. This is exactly equivalent
/// to calling the generated "Has" property corresponding to the field.
/// </summary>
/// <exception cref="ArgumentException">the field is a repeated field,
/// or it's not a field of this type</exception>
bool HasField(Descriptors.FieldDescriptor field);
/// <summary>
/// Obtains the value of the given field, or the default value if
/// it isn't set. For value type fields including enums, the boxed
/// value is returned. For embedded message fields, the sub-message
/// is returned. For repeated fields, an IList&lt;T&gt; is returned.
/// </summary>
object this[Descriptors.FieldDescriptor field] { get; }
/// <summary>
/// Returns the number of elements of a repeated field. This is
/// exactly equivalent to calling the generated "Count" property
/// corresponding to the field.
/// </summary>
/// <exception cref="ArgumentException">the field is not a repeated field,
/// or it's not a field of this type</exception>
int GetRepeatedFieldCount(Descriptors.FieldDescriptor field);
/// <summary>
/// Gets an element of a repeated field. For value type fields
/// including enums, the boxed value is returned. For embedded
/// message fields, the sub-message is returned.
/// </summary>
/// <exception cref="ArgumentException">the field is not a repeated field,
/// or it's not a field of this type</exception>
/// <exception cref="ArgumentOutOfRangeException">the index is out of
/// range for the repeated field's value</exception>
object this[Descriptors.FieldDescriptor field, int index] { get; }
/// <summary>
/// Returns the unknown fields for this message.
/// </summary>
UnknownFieldSet UnknownFields { get; }
/// <summary>
/// Returns true iff all required fields in the message and all embedded
/// messages are set.
/// </summary>
bool Initialized { get; }
/// <summary>
/// Serializes the message and writes it to the given output stream.
/// This does not flush or close the stream.
/// </summary>
/// <param name="output"></param>
void WriteTo(CodedOutputStream output);
/// <summary>
/// Returns the number of bytes required to encode this message.
/// The result is only computed on the first call and memoized after that.
/// </summary>
int SerializedSize { get; }
#region Comparison and hashing
/// <summary>
/// Compares the specified object with this message for equality.
/// Returns true iff the given object is a message of the same type
/// (as defined by DescriptorForType) and has identical values
/// for all its fields.
/// </summary>
bool Equals(object other);
/// <summary>
/// Returns the hash code value for this message.
/// TODO(jonskeet): Specify the hash algorithm, but better than the Java one!
/// </summary>
/// <returns></returns>
int GetHashCode();
#endregion
#region Convenience methods
/// <summary>
/// Converts the message to a string in protocol buffer text format.
/// This is just a trivial wrapper around TextFormat.PrintToString.
/// </summary>
string ToString();
/// <summary>
/// Serializes the message to a ByteString. This is a trivial wrapper
/// around WriteTo(CodedOutputStream).
/// </summary>
ByteString ToByteString();
/// <summary>
/// Serializes the message to a byte array. This is a trivial wrapper
/// around WriteTo(CodedOutputStream).
/// </summary>
byte[] ToByteArray();
/// <summary>
/// Serializes the message and writes it to the given stream.
/// This is just a wrapper around WriteTo(CodedOutputStream). This
/// does not flush or close the stream.
/// </summary>
/// <param name="output"></param>
void WriteTo(Stream output);
#endregion
#region Builders
/// <summary>
/// Constructs a new builder for a message of the same type as this message.
/// </summary>
IBuilder<T> NewBuilderForType();
#endregion
}
}

View File

@ -0,0 +1,7 @@
using System;
using System.IO;
namespace Google.ProtocolBuffers {
public class InvalidProtocolBufferException : IOException {
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ProtocolBuffers")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ProtocolBuffers")]
[assembly: AssemblyCopyright("Copyright © 2008")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("279b643d-70e8-47ae-9eb1-500d1c48bab6")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.21022</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{6908BDCE-D925-43F3-94AC-A531E6DF2591}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Google.ProtocolBuffers</RootNamespace>
<AssemblyName>Google.ProtocolBuffers</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ByteString.cs" />
<Compile Include="CodedInputStream.cs" />
<Compile Include="CodedOutputStream.cs" />
<Compile Include="Descriptors.cs" />
<Compile Include="ExtensionRegistry.cs" />
<Compile Include="IBuilder.cs" />
<Compile Include="IMessage.cs" />
<Compile Include="InvalidProtocolBufferException.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UninitializedMessageException.cs" />
<Compile Include="UnknownFieldSet.cs" />
<Compile Include="WireFormat.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,6 @@
using System;
namespace Google.ProtocolBuffers {
public class UninitializedMessageException : Exception {
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Google.ProtocolBuffers {
public class UnknownFieldSet {
public void WriteTo(CodedOutputStream output) {
throw new NotImplementedException();
}
public int SerializedSize { get { return 0; } }
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Google.ProtocolBuffers {
public class WireFormat {
public enum WireType {
Varint = 0,
Fixed64 = 1,
LengthDelimited = 2,
StartGroup = 3,
EndGroup = 4,
Fixed32 = 5
}
internal class MessageSetField {
internal const int Item = 1;
internal const int TypeID = 2;
internal const int Message = 3;
}
public static uint MakeTag(int fieldNumber, WireType type) {
// FIXME
return 0;
}
}
}