You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
974 lines
27 KiB
974 lines
27 KiB
#region ProtocolParser
|
|
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
using System.Security;
|
|
|
|
//
|
|
// Read/Write string and byte arrays
|
|
//
|
|
namespace BinarySerialize
|
|
{
|
|
public static partial class ProtocolParser
|
|
{
|
|
public static string ReadString(Stream stream)
|
|
{
|
|
var bytes = ReadBytes(stream);
|
|
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads a length delimited byte array
|
|
/// </summary>
|
|
public static byte[] ReadBytes(Stream stream)
|
|
{
|
|
//VarInt length
|
|
int length = (int)ReadUInt32(stream);
|
|
|
|
//Bytes
|
|
byte[] buffer = new byte[length];
|
|
int read = 0;
|
|
while (read < length)
|
|
{
|
|
int r = stream.Read(buffer, read, length - read);
|
|
if (r == 0)
|
|
throw new ProtocolBufferException("Expected " + (length - read) + " got " + read);
|
|
read += r;
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Skip the next varint length prefixed bytes.
|
|
/// Alternative to ReadBytes when the data is not of interest.
|
|
/// </summary>
|
|
public static void SkipBytes(Stream stream)
|
|
{
|
|
int length = (int)ReadUInt32(stream);
|
|
if (stream.CanSeek)
|
|
stream.Seek(length, SeekOrigin.Current);
|
|
else
|
|
ReadBytes(stream);
|
|
}
|
|
|
|
public static void WriteString(Stream stream, string val)
|
|
{
|
|
WriteBytes(stream, Encoding.UTF8.GetBytes(val));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes length delimited byte array
|
|
/// </summary>
|
|
public static void WriteBytes(Stream stream, byte[] val)
|
|
{
|
|
WriteUInt32(stream, (uint)val.Length);
|
|
stream.Write(val, 0, val.Length);
|
|
}
|
|
}
|
|
|
|
[Obsolete("Renamed to PositionStream")]
|
|
public class StreamRead : PositionStream
|
|
{
|
|
public StreamRead(Stream baseStream) : base(baseStream)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wrapper for streams that does not support the Position property.
|
|
/// Adds support for the Position property.
|
|
/// </summary>
|
|
public class PositionStream : Stream
|
|
{
|
|
readonly Stream stream;
|
|
|
|
/// <summary>
|
|
/// Bytes read in the stream starting from the beginning.
|
|
/// </summary>
|
|
public int BytesRead { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Define how many bytes are allowed to read
|
|
/// </summary>
|
|
/// <param name='baseStream'>
|
|
/// Base stream.
|
|
/// </param>
|
|
/// <param name='maxLength'>
|
|
/// Max length allowed to read from the stream.
|
|
/// </param>
|
|
public PositionStream(Stream baseStream)
|
|
{
|
|
this.stream = baseStream;
|
|
}
|
|
public PositionStream(byte[] arr)
|
|
{
|
|
this.stream = new MemoryStream(arr);
|
|
}
|
|
public override void Flush()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
int read = stream.Read(buffer, offset, count);
|
|
BytesRead += read;
|
|
return read;
|
|
}
|
|
|
|
public override int ReadByte()
|
|
{
|
|
int b = stream.ReadByte();
|
|
BytesRead += 1;
|
|
return b;
|
|
}
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
if (stream.CanSeek)
|
|
return stream.Seek(offset, origin);
|
|
|
|
if (origin == SeekOrigin.Current && offset >= 0)
|
|
{
|
|
var buffer = new byte[Math.Min(offset, 10000)];
|
|
long end = BytesRead + offset;
|
|
while (BytesRead < end)
|
|
{
|
|
int read = stream.Read(buffer, 0, (int)Math.Min(end - BytesRead, buffer.Length));
|
|
if (read == 0)
|
|
break;
|
|
BytesRead += read;
|
|
}
|
|
return BytesRead;
|
|
}
|
|
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override bool CanRead
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public override bool CanSeek
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override bool CanWrite
|
|
{
|
|
get
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override long Length
|
|
{
|
|
get
|
|
{
|
|
return stream.Length;
|
|
}
|
|
}
|
|
|
|
public override long Position
|
|
{
|
|
get
|
|
{
|
|
return this.BytesRead;
|
|
}
|
|
set
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
stream.Dispose();
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
#region ProtocolParserExceptions
|
|
//
|
|
// Exception used in the generated code
|
|
//
|
|
|
|
namespace BinarySerialize
|
|
{
|
|
///<summary>>
|
|
/// This exception is thrown when badly formatted protocol buffer data is read.
|
|
///</summary>
|
|
public class ProtocolBufferException : Exception
|
|
{
|
|
public ProtocolBufferException(string message) : base(message)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
#region ProtocolParserFixed
|
|
//
|
|
// This file contain references on how to write and read
|
|
// fixed integers and float/double.
|
|
//
|
|
|
|
namespace BinarySerialize
|
|
{
|
|
public static partial class ProtocolParser
|
|
{
|
|
#region Fixed Int, Only for reference
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static ulong ReadFixed64(BinaryReader reader)
|
|
{
|
|
return reader.ReadUInt64();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static long ReadSFixed64(BinaryReader reader)
|
|
{
|
|
return reader.ReadInt64();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static uint ReadFixed32(BinaryReader reader)
|
|
{
|
|
return reader.ReadUInt32();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static int ReadSFixed32(BinaryReader reader)
|
|
{
|
|
return reader.ReadInt32();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static void WriteFixed64(BinaryWriter writer, ulong val)
|
|
{
|
|
writer.Write(val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static void WriteSFixed64(BinaryWriter writer, long val)
|
|
{
|
|
writer.Write(val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static void WriteFixed32(BinaryWriter writer, uint val)
|
|
{
|
|
writer.Write(val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static void WriteSFixed32(BinaryWriter writer, int val)
|
|
{
|
|
writer.Write(val);
|
|
}
|
|
|
|
#endregion Fixed Int, Only for reference
|
|
|
|
#region Fixed: float, double. Only for reference
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static float ReadFloat(BinaryReader reader)
|
|
{
|
|
return reader.ReadSingle();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static double ReadDouble(BinaryReader reader)
|
|
{
|
|
return reader.ReadDouble();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static void WriteFloat(BinaryWriter writer, float val)
|
|
{
|
|
writer.Write(val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only for reference
|
|
/// </summary>
|
|
[Obsolete("Only for reference")]
|
|
public static void WriteDouble(BinaryWriter writer, double val)
|
|
{
|
|
writer.Write(val);
|
|
}
|
|
|
|
#endregion Fixed: float, double. Only for reference
|
|
}
|
|
}
|
|
#endregion
|
|
#region ProtocolParserKey
|
|
//
|
|
// Reader/Writer for field key
|
|
//
|
|
|
|
namespace BinarySerialize
|
|
{
|
|
public enum Wire
|
|
{
|
|
Varint = 0, //int32, int64, UInt32, UInt64, SInt32, SInt64, bool, enum
|
|
Fixed64 = 1, //fixed64, sfixed64, double
|
|
LengthDelimited = 2, //string, bytes, embedded messages, packed repeated fields
|
|
//Start = 3, // groups (deprecated)
|
|
//End = 4, // groups (deprecated)
|
|
Fixed32 = 5, //32-bit fixed32, SFixed32, float
|
|
}
|
|
|
|
public class Key
|
|
{
|
|
public uint Field { get; set; }
|
|
|
|
public Wire WireType { get; set; }
|
|
|
|
public Key(uint field, Wire wireType)
|
|
{
|
|
this.Field = field;
|
|
this.WireType = wireType;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("[Key: {0}, {1}]", Field, WireType);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Storage of unknown fields
|
|
/// </summary>
|
|
public class KeyValue
|
|
{
|
|
public Key Key { get; set; }
|
|
|
|
public byte[] Value { get; set; }
|
|
|
|
public KeyValue(Key key, byte[] value)
|
|
{
|
|
this.Key = key;
|
|
this.Value = value;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("[KeyValue: {0}, {1}, {2} bytes]", Key.Field, Key.WireType, Value.Length);
|
|
}
|
|
}
|
|
|
|
public static partial class ProtocolParser
|
|
{
|
|
public static Key ReadKey(Stream stream)
|
|
{
|
|
uint n = ReadUInt32(stream);
|
|
return new Key(n >> 3, (Wire)(n & 0x07));
|
|
}
|
|
|
|
public static Key ReadKey(byte firstByte, Stream stream)
|
|
{
|
|
if (firstByte < 128)
|
|
return new Key((uint)(firstByte >> 3), (Wire)(firstByte & 0x07));
|
|
uint fieldID = ((uint)ReadUInt32(stream) << 4) | ((uint)(firstByte >> 3) & 0x0F);
|
|
return new Key(fieldID, (Wire)(firstByte & 0x07));
|
|
}
|
|
|
|
public static void WriteKey(Stream stream, Key key)
|
|
{
|
|
uint n = (key.Field << 3) | ((uint)key.WireType);
|
|
WriteUInt32(stream, n);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Seek past the value for the previously read key.
|
|
/// </summary>
|
|
public static void SkipKey(Stream stream, Key key)
|
|
{
|
|
switch (key.WireType)
|
|
{
|
|
case Wire.Fixed32:
|
|
stream.Seek(4, SeekOrigin.Current);
|
|
return;
|
|
case Wire.Fixed64:
|
|
stream.Seek(8, SeekOrigin.Current);
|
|
return;
|
|
case Wire.LengthDelimited:
|
|
stream.Seek(ProtocolParser.ReadUInt32(stream), SeekOrigin.Current);
|
|
return;
|
|
case Wire.Varint:
|
|
ProtocolParser.ReadSkipVarInt(stream);
|
|
return;
|
|
default:
|
|
throw new ProtocolBufferException("Unknown wire type: " + key.WireType);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read the value for an unknown key as bytes.
|
|
/// Used to preserve unknown keys during deserialization.
|
|
/// Requires the message option preserveunknown=true.
|
|
/// </summary>
|
|
public static byte[] ReadValueBytes(Stream stream, Key key)
|
|
{
|
|
byte[] b;
|
|
int offset = 0;
|
|
|
|
switch (key.WireType)
|
|
{
|
|
case Wire.Fixed32:
|
|
b = new byte[4];
|
|
while (offset < 4)
|
|
offset += stream.Read(b, offset, 4 - offset);
|
|
return b;
|
|
case Wire.Fixed64:
|
|
b = new byte[8];
|
|
while (offset < 8)
|
|
offset += stream.Read(b, offset, 8 - offset);
|
|
return b;
|
|
case Wire.LengthDelimited:
|
|
//Read and include length in value buffer
|
|
uint length = ProtocolParser.ReadUInt32(stream);
|
|
using (var ms = new MemoryStream())
|
|
{
|
|
//TODO: pass b directly to MemoryStream constructor or skip usage of it completely
|
|
ProtocolParser.WriteUInt32(ms, length);
|
|
b = new byte[length + ms.Length];
|
|
ms.ToArray().CopyTo(b, 0);
|
|
offset = (int)ms.Length;
|
|
}
|
|
|
|
//Read data into buffer
|
|
while (offset < b.Length)
|
|
offset += stream.Read(b, offset, b.Length - offset);
|
|
return b;
|
|
case Wire.Varint:
|
|
return ProtocolParser.ReadVarIntBytes(stream);
|
|
default:
|
|
throw new ProtocolBufferException("Unknown wire type: " + key.WireType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
#region ProtocolParserMemory
|
|
|
|
//
|
|
// MemoryStream management
|
|
//
|
|
namespace BinarySerialize
|
|
{
|
|
public interface MemoryStreamStack : IDisposable
|
|
{
|
|
MemoryStream Pop();
|
|
|
|
void Push(MemoryStream stream);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Thread safe stack of memory streams
|
|
/// </summary>
|
|
public class ThreadSafeStack : MemoryStreamStack
|
|
{
|
|
readonly Stack<MemoryStream> stack = new Stack<MemoryStream>();
|
|
|
|
/// <summary>
|
|
/// The returned stream is not reset.
|
|
/// You must call .SetLength(0) before using it.
|
|
/// This is done in the generated code.
|
|
/// </summary>
|
|
public MemoryStream Pop()
|
|
{
|
|
lock (stack)
|
|
{
|
|
if (stack.Count == 0)
|
|
return new MemoryStream();
|
|
else
|
|
return stack.Pop();
|
|
}
|
|
}
|
|
|
|
public void Push(MemoryStream stream)
|
|
{
|
|
lock (stack)
|
|
{
|
|
stack.Push(stream);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
lock (stack)
|
|
{
|
|
stack.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Non-thread safe stack of memory streams
|
|
/// Safe as long as only one thread is Serializing
|
|
/// </summary>
|
|
public class ThreadUnsafeStack : MemoryStreamStack
|
|
{
|
|
readonly Stack<MemoryStream> stack = new Stack<MemoryStream>();
|
|
|
|
/// <summary>
|
|
/// The returned stream is not reset.
|
|
/// You must call .SetLength(0) before using it.
|
|
/// This is done in the generated code.
|
|
/// </summary>
|
|
public MemoryStream Pop()
|
|
{
|
|
if (stack.Count == 0)
|
|
return new MemoryStream();
|
|
else
|
|
return stack.Pop();
|
|
}
|
|
|
|
public void Push(MemoryStream stream)
|
|
{
|
|
stack.Push(stream);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
stack.Clear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unoptimized stack, allocates a new MemoryStream for every request.
|
|
/// </summary>
|
|
public class AllocationStack : MemoryStreamStack
|
|
{
|
|
/// <summary>
|
|
/// The returned stream is not reset.
|
|
/// You must call .SetLength(0) before using it.
|
|
/// This is done in the generated code.
|
|
/// </summary>
|
|
public MemoryStream Pop()
|
|
{
|
|
return new MemoryStream();
|
|
}
|
|
|
|
public void Push(MemoryStream stream)
|
|
{
|
|
//No need to Dispose MemoryStream
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
}
|
|
|
|
public static partial class ProtocolParser
|
|
{
|
|
/// <summary>
|
|
/// Experimental stack of MemoryStream
|
|
/// </summary>
|
|
public static MemoryStreamStack Stack = new AllocationStack();
|
|
}
|
|
}
|
|
#endregion
|
|
#region ProtocolParserMemory4
|
|
|
|
//
|
|
// MemoryStream management.
|
|
// .NET 4 features not added when the --net3 flag is being used
|
|
//
|
|
namespace BinarySerialize
|
|
{
|
|
using System.Collections.Concurrent;
|
|
|
|
public class ConcurrentBagStack : MemoryStreamStack
|
|
{
|
|
ConcurrentBag<MemoryStream> bag = new ConcurrentBag<MemoryStream>();
|
|
|
|
/// <summary>
|
|
/// The returned stream is not reset.
|
|
/// You must call .SetLength(0) before using it.
|
|
/// This is done in the generated code.
|
|
/// </summary>
|
|
public MemoryStream Pop()
|
|
{
|
|
MemoryStream result;
|
|
|
|
if (bag.TryTake(out result))
|
|
return result;
|
|
else
|
|
return new MemoryStream();
|
|
}
|
|
|
|
public void Push(MemoryStream stream)
|
|
{
|
|
bag.Add(stream);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
throw new InvalidOperationException("ConcurrentBagStack.Dispose() should not be called.");
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
#region ProtocolParserVarInt
|
|
|
|
namespace BinarySerialize
|
|
{
|
|
public static partial class ProtocolParser
|
|
{
|
|
/// <summary>
|
|
/// Reads past a varint for an unknown field.
|
|
/// </summary>
|
|
public static void ReadSkipVarInt(Stream stream)
|
|
{
|
|
while (true)
|
|
{
|
|
int b = stream.ReadByte();
|
|
if (b < 0)
|
|
throw new IOException("Stream ended too early");
|
|
|
|
if ((b & 0x80) == 0)
|
|
return; //end of varint
|
|
}
|
|
}
|
|
|
|
public static byte[] ReadVarIntBytes(Stream stream)
|
|
{
|
|
byte[] buffer = new byte[10];
|
|
int offset = 0;
|
|
while (true)
|
|
{
|
|
int b = stream.ReadByte();
|
|
if (b < 0)
|
|
throw new IOException("Stream ended too early");
|
|
buffer[offset] = (byte)b;
|
|
offset += 1;
|
|
if ((b & 0x80) == 0)
|
|
break; //end of varint
|
|
if (offset >= buffer.Length)
|
|
throw new ProtocolBufferException("VarInt too long, more than 10 bytes");
|
|
}
|
|
byte[] ret = new byte[offset];
|
|
Array.Copy(buffer, ret, ret.Length);
|
|
return ret;
|
|
}
|
|
|
|
#region VarInt: int32, uint32, sint32
|
|
|
|
/// <summary>
|
|
/// Since the int32 format is inefficient for negative numbers we have avoided to implement it.
|
|
/// The same functionality can be achieved using: (int)ReadUInt64(stream);
|
|
/// </summary>
|
|
[Obsolete("Use (int)ReadUInt64(stream); //yes 64")]
|
|
public static int ReadInt32(Stream stream)
|
|
{
|
|
return (int)ReadUInt64(stream);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Since the int32 format is inefficient for negative numbers we have avoided to imlplement.
|
|
/// The same functionality can be achieved using: WriteUInt64(stream, (uint)val);
|
|
/// Note that 64 must always be used for int32 to generate the ten byte wire format.
|
|
/// </summary>
|
|
[Obsolete("Use WriteUInt64(stream, (ulong)val); //yes 64, negative numbers are encoded that way")]
|
|
public static void WriteInt32(Stream stream, int val)
|
|
{
|
|
//signed varint is always encoded as 64 but values!
|
|
WriteUInt64(stream, (ulong)val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zig-zag signed VarInt format
|
|
/// </summary>
|
|
public static int ReadZInt32(Stream stream)
|
|
{
|
|
uint val = ReadUInt32(stream);
|
|
return (int)(val >> 1) ^ ((int)(val << 31) >> 31);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zig-zag signed VarInt format
|
|
/// </summary>
|
|
public static void WriteZInt32(Stream stream, int val)
|
|
{
|
|
WriteUInt32(stream, (uint)((val << 1) ^ (val >> 31)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unsigned VarInt format
|
|
/// Do not use to read int32, use ReadUint64 for that.
|
|
/// </summary>
|
|
public static uint ReadUInt32(Stream stream)
|
|
{
|
|
uint val = 0;
|
|
|
|
for (int n = 0; n < 5; n++)
|
|
{
|
|
int b = stream.ReadByte();
|
|
if (b < 0)
|
|
throw new IOException("Stream ended too early");
|
|
|
|
//Check that it fits in 32 bits
|
|
if ((n == 4) && (b & 0xF0) != 0)
|
|
throw new ProtocolBufferException("Got larger VarInt than 32bit unsigned");
|
|
//End of check
|
|
|
|
if ((b & 0x80) == 0)
|
|
return val | (uint)b << (7 * n);
|
|
|
|
val |= (uint)(b & 0x7F) << (7 * n);
|
|
}
|
|
|
|
throw new ProtocolBufferException("Got larger VarInt than 32bit unsigned");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unsigned VarInt format
|
|
/// </summary>
|
|
public static void WriteUInt32(Stream stream, uint val)
|
|
{
|
|
while (true)
|
|
{
|
|
byte b = (byte)(val & 0x7F);
|
|
val = val >> 7;
|
|
if (val == 0)
|
|
{
|
|
stream.WriteByte(b);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
b |= 0x80;
|
|
stream.WriteByte(b);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion VarInt: int32, uint32, sint32
|
|
|
|
#region VarInt: int64, UInt64, SInt64
|
|
|
|
/// <summary>
|
|
/// Since the int64 format is inefficient for negative numbers we have avoided to implement it.
|
|
/// The same functionality can be achieved using: (long)ReadUInt64(stream);
|
|
/// </summary>
|
|
[Obsolete("Use (long)ReadUInt64(stream); instead")]
|
|
public static int ReadInt64(Stream stream)
|
|
{
|
|
return (int)ReadUInt64(stream);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Since the int64 format is inefficient for negative numbers we have avoided to implement.
|
|
/// The same functionality can be achieved using: WriteUInt64 (stream, (ulong)val);
|
|
/// </summary>
|
|
[Obsolete("Use WriteUInt64 (stream, (ulong)val); instead")]
|
|
public static void WriteInt64(Stream stream, int val)
|
|
{
|
|
WriteUInt64(stream, (ulong)val);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zig-zag signed VarInt format
|
|
/// </summary>
|
|
public static long ReadZInt64(Stream stream)
|
|
{
|
|
ulong val = ReadUInt64(stream);
|
|
return (long)(val >> 1) ^ ((long)(val << 63) >> 63);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Zig-zag signed VarInt format
|
|
/// </summary>
|
|
public static void WriteZInt64(Stream stream, long val)
|
|
{
|
|
WriteUInt64(stream, (ulong)((val << 1) ^ (val >> 63)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unsigned VarInt format
|
|
/// </summary>
|
|
public static ulong ReadUInt64(Stream stream)
|
|
{
|
|
ulong val = 0;
|
|
|
|
for (int n = 0; n < 10; n++)
|
|
{
|
|
int b = stream.ReadByte();
|
|
if (b < 0)
|
|
throw new IOException("Stream ended too early");
|
|
|
|
//Check that it fits in 64 bits
|
|
if ((n == 9) && (b & 0xFE) != 0)
|
|
throw new ProtocolBufferException("Got larger VarInt than 64 bit unsigned");
|
|
//End of check
|
|
|
|
if ((b & 0x80) == 0)
|
|
return val | (ulong)b << (7 * n);
|
|
|
|
val |= (ulong)(b & 0x7F) << (7 * n);
|
|
}
|
|
|
|
throw new ProtocolBufferException("Got larger VarInt than 64 bit unsigned");
|
|
}
|
|
|
|
public static unsafe float ReadFloat(Stream stream)
|
|
{
|
|
var buffer = FillBuffer(stream, 4);
|
|
uint v = (uint) ((int) buffer[0] | (int) buffer[1] << 8 | (int) buffer[2] << 16 | (int) buffer[3] << 24);
|
|
return *(float*) &v;
|
|
}
|
|
|
|
public static unsafe double ReadDouble(Stream stream)
|
|
{
|
|
var buffer = FillBuffer(stream,8);
|
|
ulong v = ((ulong) (uint) ((int) buffer[4] | (int) buffer[5] << 8 | (int) buffer[6] << 16 | (int) buffer[7] << 24) << 32 | (ulong) (uint) ((int) buffer[0] | (int) buffer[1] << 8 | (int) buffer[2] << 16 | (int) buffer[3] << 24));
|
|
return *(double*)&v;
|
|
}
|
|
|
|
private static byte[] FillBuffer(Stream stream,int numBytes)
|
|
{
|
|
var _buffer = new byte[numBytes];
|
|
int offset = 0;
|
|
if (numBytes == 1)
|
|
{
|
|
int num = stream.ReadByte();
|
|
_buffer[0] = (byte)num;
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
int num = stream.Read(_buffer, offset, numBytes - offset);
|
|
offset += num;
|
|
}while (offset < numBytes);
|
|
}
|
|
|
|
return _buffer;
|
|
}
|
|
/// <summary>
|
|
/// Unsigned VarInt format
|
|
/// </summary>
|
|
public static void WriteUInt64(Stream stream, ulong val)
|
|
{
|
|
while (true)
|
|
{
|
|
byte b = (byte)(val & 0x7F);
|
|
val = val >> 7;
|
|
if (val == 0)
|
|
{
|
|
stream.WriteByte(b);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
b |= 0x80;
|
|
stream.WriteByte(b);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static unsafe void WriteFloat(Stream stream, float val)
|
|
{
|
|
var _buffer = new byte[4];
|
|
uint num = *(uint*)&val;
|
|
_buffer[0] = (byte)num;
|
|
_buffer[1] = (byte)(num >> 8);
|
|
_buffer[2] = (byte)(num >> 16);
|
|
_buffer[3] = (byte)(num >> 24);
|
|
stream.Write(_buffer, 0, 4);
|
|
}
|
|
|
|
public static unsafe void WriteDouble(Stream stream, double val)
|
|
{
|
|
var _buffer = new byte[8];
|
|
ulong num = (ulong)*(long*)&val;
|
|
_buffer[0] = (byte)num;
|
|
_buffer[1] = (byte)(num >> 8);
|
|
_buffer[2] = (byte)(num >> 16);
|
|
_buffer[3] = (byte)(num >> 24);
|
|
_buffer[4] = (byte)(num >> 32);
|
|
_buffer[5] = (byte)(num >> 40);
|
|
_buffer[6] = (byte)(num >> 48);
|
|
_buffer[7] = (byte)(num >> 56);
|
|
stream.Write(_buffer, 0, 8);
|
|
}
|
|
|
|
#endregion VarInt: int64, UInt64, SInt64
|
|
|
|
#region Varint: bool
|
|
|
|
public static bool ReadBool(Stream stream)
|
|
{
|
|
int b = stream.ReadByte();
|
|
if (b < 0)
|
|
throw new IOException("Stream ended too early");
|
|
if (b == 1)
|
|
return true;
|
|
if (b == 0)
|
|
return false;
|
|
throw new ProtocolBufferException("Invalid boolean value");
|
|
}
|
|
|
|
public static void WriteBool(Stream stream, bool val)
|
|
{
|
|
stream.WriteByte(val ? (byte)1 : (byte)0);
|
|
}
|
|
|
|
#endregion Varint: bool
|
|
}
|
|
}
|
|
#endregion
|
|
|