changeset 11:2e0ff842aa4a

Added Protocol Buffers encoding/decoding code
author Ivo Smits <Ivo@UCIS.nl>
date Mon, 04 Feb 2013 22:47:01 +0100
parents 7269e91c6e26
children ba8f94212c6e
files ProtocolBuffers.cs UCIS.csproj
diffstat 2 files changed, 188 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ProtocolBuffers.cs	Mon Feb 04 22:47:01 2013 +0100
@@ -0,0 +1,187 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace UCIS.ProtocolBuffers {
+	public interface IPBReader {
+		void Reset();
+		Boolean NextField();
+		int FieldNumber { get; }
+		int WireType { get; }
+		Int64 GetVarint();
+		Int64 GetFixed64();
+		Byte[] GetBytes();
+		String GetString();
+		IPBReader GetMessageReader();
+		Int32 GetFixed32();
+	}
+	public interface IPBWriter {
+		void WriteVarint(int fieldNumber, Int64 value);
+		void WriteFixed64(int fieldNumber, Int64 value);
+		void WriteBytes(int fieldNumber, Byte[] bytes);
+		void WriteBytes(int fieldNumber, Byte[] bytes, int offset, int count);
+		void WriteFixed32(int fieldNumber, Int32 value);
+	}
+	public interface IPBMessage {
+		void Decode(IPBReader r);
+		void Encode(IPBWriter w);
+	}
+	public class ArrayPBReader : IPBReader {
+		Byte[] buffer;
+		int offset, length, index;
+		int currentField;
+		Boolean hasCurrentField;
+		int currentFieldLength;
+
+		public ArrayPBReader(Byte[] buffer) : this(buffer, 0, buffer.Length) { }
+		public ArrayPBReader(Byte[] buffer, int offset, int length) {
+			this.buffer = buffer;
+			this.offset = offset;
+			this.length = length;
+			Reset();
+		}
+
+		public void Reset() {
+			index = 0;
+			hasCurrentField = false;
+		}
+
+		public bool NextField() {
+			if (hasCurrentField) {
+				index += currentFieldLength;
+				hasCurrentField = false;
+			}
+			if (index >= length) return false;
+			index += ReadVarIntTo(out currentField);
+			int dummy;
+			switch (WireType) {
+				case 0: currentFieldLength = ReadVarIntTo(out dummy); ; break; //Varint
+				case 1: currentFieldLength = 8; break; //64bit
+				case 2: index += ReadVarIntTo(out currentFieldLength); break; //Bytes
+				case 5: currentFieldLength = 4; break; //32bit
+				default: throw new InvalidDataException();
+			}
+			if (index + currentFieldLength > length) throw new InvalidDataException();
+			hasCurrentField = true;
+			return true;
+		}
+
+		private int ReadVarIntTo(out int v) {
+			v = 0;
+			int h = 0;
+			int b = 0x80;
+			int l = 0;
+			while ((b & 0x80) != 0) {
+				b = buffer[offset + index + l];
+				v |= (b & 0x7F) << h;
+				h += 7;
+				l++;
+			}
+			return l;
+		}
+
+		public int FieldNumber { get { return (int)((UInt32)currentField >> 3); } }
+		public int WireType { get { return currentField & 7; } }
+
+		public long GetVarint() {
+			if (!hasCurrentField || WireType != 0) throw new InvalidOperationException();
+			int v;
+			ReadVarIntTo(out v);
+			return v;
+		}
+
+		public long GetFixed64() {
+			if (!hasCurrentField || WireType != 1) throw new InvalidOperationException();
+			int i = offset + index;
+			return ((long)buffer[i + 0] << 0) | ((long)buffer[i + 1] << 8) | ((long)buffer[i + 2] << 16) | ((long)buffer[i + 3] << 24) |
+				((long)buffer[i + 4] << 32) | ((long)buffer[i + 5] << 40) | ((long)buffer[i + 6] << 48) | ((long)buffer[i + 7] << 56);
+		}
+
+		public byte[] GetBytes() {
+			if (!hasCurrentField || WireType != 2) throw new InvalidOperationException();
+			Byte[] bytes = new Byte[currentFieldLength];
+			Buffer.BlockCopy(buffer, offset + index, bytes, 0, currentFieldLength);
+			return bytes;
+		}
+
+		public string GetString() {
+			if (!hasCurrentField || WireType != 2) throw new InvalidOperationException();
+			return Encoding.UTF8.GetString(buffer, offset + index, currentFieldLength);
+		}
+
+		public IPBReader GetMessageReader() {
+			if (!hasCurrentField || WireType != 2) throw new InvalidOperationException();
+			return new ArrayPBReader(buffer, offset + index, currentFieldLength);
+		}
+
+		public int GetFixed32() {
+			if (!hasCurrentField || WireType != 5) throw new InvalidOperationException();
+			int i = offset + index;
+			return ((int)buffer[i + 0] << 0) | ((int)buffer[i + 1] << 8) | ((int)buffer[i + 2] << 16) | ((int)buffer[i + 3] << 24);
+		}
+	}
+	public class StreamPBWriter : IPBWriter {
+		Stream stream;
+
+		public StreamPBWriter(Stream stream) {
+			this.stream = stream;
+		}
+
+		public static Byte[] Encode(IPBMessage message) {
+			using (MemoryStream ms = new MemoryStream()) {
+				message.Encode(new StreamPBWriter(ms));
+				return ms.ToArray();
+			}
+		}
+
+		public static int EncodeToStreamBuffered(Stream stream, IPBMessage message) {
+			using (MemoryStream ms = new MemoryStream()) {
+				message.Encode(new StreamPBWriter(ms));
+				ms.WriteTo(stream);
+				return (int)ms.Length;
+			}
+		}
+
+		private void WriteVarint(long value) {
+			while ((value & ~0x7fL) != 0) {
+				stream.WriteByte((Byte)(0x80 | value));
+				value >>= 7;
+			}
+			stream.WriteByte((Byte)value);
+		}
+		private void WriteTag(int fieldNumber, int wireType) {
+			WriteVarint((fieldNumber << 3) | wireType);
+		}
+
+		public void WriteVarint(int fieldNumber, long value) {
+			WriteTag(fieldNumber, 0);
+			WriteVarint(value);
+		}
+
+		public void WriteFixed64(int fieldNumber, long value) {
+			WriteTag(fieldNumber, 1);
+			for (int i = 0; i < 8; i++) {
+				stream.WriteByte((Byte)value);
+				value >>= 8;
+			}
+		}
+
+		public void WriteBytes(int fieldNumber, byte[] bytes) {
+			WriteBytes(fieldNumber, bytes, 0, bytes.Length);
+		}
+
+		public void WriteBytes(int fieldNumber, byte[] bytes, int offset, int count) {
+			WriteTag(fieldNumber, 2);
+			WriteVarint(count);
+			stream.Write(bytes, offset, count);
+		}
+
+		public void WriteFixed32(int fieldNumber, int value) {
+			WriteTag(fieldNumber, 5);
+			for (int i = 0; i < 4; i++) {
+				stream.WriteByte((Byte)value);
+				value >>= 8;
+			}
+		}
+	}
+}
\ No newline at end of file
--- a/UCIS.csproj	Fri Jan 18 18:24:20 2013 +0100
+++ b/UCIS.csproj	Mon Feb 04 22:47:01 2013 +0100
@@ -77,6 +77,7 @@
     <Compile Include="Pml\RW\PmlXmlRW.cs" />
     <Compile Include="Pml\Channels\TCPPmlChannel.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ProtocolBuffers.cs" />
     <Compile Include="Radio\Tuner.cs" />
     <Compile Include="ThreadPool.cs" />
     <Compile Include="UTF8NoPreamble.cs" />