using System;
using System.Collections.Generic;
using System.Globalization;
using Google.ProtocolBuffers.Descriptors;
//Disable CS3011: only CLS-compliant members can be abstract
#pragma warning disable 3011
namespace Google.ProtocolBuffers.Serialization
{
/// <summary>
/// Provides a base-class that provides some basic functionality for handling type dispatching
/// </summary>
public abstract class AbstractReader : ICodedInputStream
{
private const int DefaultMaxDepth = 64;
private int _depth;
/// <summary> Constructs a new reader </summary>
protected AbstractReader() { MaxDepth = DefaultMaxDepth; }
/// <summary> Gets or sets the maximum recursion depth allowed </summary>
public int MaxDepth { get; set; }
/// <summary>
/// Merges the contents of stream into the provided message builder
/// </summary>
public TBuilder Merge<TBuilder>(TBuilder builder) where TBuilder : IBuilderLite
{
return Merge(builder, ExtensionRegistry.Empty);
}
/// <summary>
/// Merges the contents of stream into the provided message builder
/// </summary>
public abstract TBuilder Merge<TBuilder>(TBuilder builder, ExtensionRegistry registry)
where TBuilder : IBuilderLite;
/// <summary>
/// Peeks at the next field in the input stream and returns what information is available.
/// </summary>
/// <remarks>
/// This may be called multiple times without actually reading the field. Only after the field
/// is either read, or skipped, should PeekNext return a different value.
/// </remarks>
protected abstract bool PeekNext(out string field);
/// <summary>
/// Causes the reader to skip past this field
/// </summary>
protected abstract void Skip();
/// <summary>
/// Returns true if it was able to read a Boolean from the input
/// </summary>
protected abstract bool Read(ref bool value);
/// <summary>
/// Returns true if it was able to read a Int32 from the input
/// </summary>
protected abstract bool Read(ref int value);
/// <summary>
/// Returns true if it was able to read a UInt32 from the input
/// </summary>
[CLSCompliant(false)]
protected abstract bool Read(ref uint value);
/// <summary>
/// Returns true if it was able to read a Int64 from the input
/// </summary>
protected abstract bool Read(ref long value);
/// <summary>
/// Returns true if it was able to read a UInt64 from the input
/// </summary>
[CLSCompliant(false)]
protected abstract bool Read(ref ulong value);
/// <summary>
/// Returns true if it was able to read a Single from the input
/// </summary>
protected abstract bool Read(ref float value);
/// <summary>
/// Returns true if it was able to read a Double from the input
/// </summary>
protected abstract bool Read(ref double value);
/// <summary>
/// Returns true if it was able to read a String from the input
/// </summary>
protected abstract bool Read(ref string value);
/// <summary>
/// Returns true if it was able to read a ByteString from the input
/// </summary>
protected abstract bool Read(ref ByteString value);
/// <summary>
/// returns true if it was able to read a single value into the value reference. The value
/// stored may be of type System.String, System.Int32, or an IEnumLite from the IEnumLiteMap.
/// </summary>
protected abstract bool ReadEnum(ref object value);
/// <summary>
/// Merges the input stream into the provided IBuilderLite
/// </summary>
protected abstract bool ReadMessage(IBuilderLite builder, ExtensionRegistry registry);
/// <summary>
/// Reads the root-message preamble specific to this formatter
/// </summary>
public abstract void ReadMessageStart();
/// <summary>
/// Reads the root-message close specific to this formatter
/// </summary>
public abstract void ReadMessageEnd();
/// <summary>
/// Merges the input stream into the provided IBuilderLite
/// </summary>
public virtual bool ReadGroup(IBuilderLite value, ExtensionRegistry registry)
{
return ReadMessage(value, registry);
}
/// <summary>
/// Cursors through the array elements and stops at the end of the array
/// </summary>
protected virtual IEnumerable<string> ForeachArrayItem(string field)
{
string next = field;
while (true)
{
yield return next;
if (!PeekNext(out next) || next != field)
{
break;
}
}
}
/// <summary>
/// Reads an array of T messages
/// </summary>
public virtual bool ReadMessageArray<T>(string field, ICollection<T> items, IMessageLite messageType,
ExtensionRegistry registry)
{
bool success = false;
foreach (string next in ForeachArrayItem(field))
{
IBuilderLite builder = messageType.WeakCreateBuilderForType();
if (ReadMessage(builder, registry))
{
items.Add((T) builder.WeakBuild());
success |= true;
}
}
return success;
}
/// <summary>
/// Reads an array of T messages as a proto-buffer group
/// </summary>
public virtual bool ReadGroupArray<T>(string field, ICollection<T> items, IMessageLite messageType,
ExtensionRegistry registry)
{
bool success = false;
foreach (string next in ForeachArrayItem(field))
{
IBuilderLite builder = messageType.WeakCreateBuilderForType();
if (ReadGroup(builder, registry))
{
items.Add((T) builder.WeakBuild());
success |= true;
}
}
return success;
}
/// <summary>
/// Reads an array of System.Enum type T and adds them to the collection
/// </summary>
public virtual bool ReadEnumArray(string field, ICollection<object> items)
{
bool success = false;
foreach (string next in ForeachArrayItem(field))
{
object temp = null;
if (ReadEnum(ref temp))
{
items.Add(temp);
success |= true;
}
}
return success;
}
/// <summary>
/// Reads an array of T, where T is a primitive type defined by FieldType
/// </summary>
public virtual bool ReadArray<T>(FieldType type, string field, ICollection<T> items)
{
bool success = false;
foreach (string next in ForeachArrayItem(field))
{
object temp = null;
if (ReadField(type, ref temp))
{
items.Add((T) temp);
success |= true;
}
}
return success;
}
/// <summary>
/// returns true if it was able to read a single primitive value of FieldType into the value reference
/// </summary>
public virtual bool ReadField(FieldType type, ref object value)
{
switch (type)
{
case FieldType.Bool:
{
bool temp = false;
if (Read(ref temp))
{
value = temp;
}
else
{
return false;
}
break;
}
case FieldType.Int64:
case FieldType.SInt64:
case FieldType.SFixed64:
{
long temp = 0;
if (Read(ref temp))
{
value = temp;
}
else
{
return false;
}
break;
}
case FieldType.UInt64:
case FieldType.Fixed64:
{
ulong temp = 0;
if (Read(ref temp))
{
value = temp;
}
else
{
return false;
}
break;
}
case FieldType.Int32:
case FieldType.SInt32:
case FieldType.SFixed32:
{
int temp = 0;
if (Read(ref temp))
{
value = temp;
}
else
{
return false;
}
break;
}
case FieldType.UInt32:
case FieldType.Fixed32:
{
uint temp = 0;
if (Read(ref temp))
{
value = temp;
}
else
{
return false;
}
break;
}
case FieldType.Float:
{
float temp = float.NaN;
if (Read(ref temp))
{
value = temp;
}
else
{
return false;
}
break;
}
case FieldType.Double:
{
double temp = float.NaN;
if (Read(ref temp))
{
value = temp;
}
else
{
return false;
}
break;
}
case FieldType.String:
{
string temp = null;
if (Read(ref temp))
{
value = temp;
}
else
{
return false;
}
break;
}
case FieldType.Bytes:
{
ByteString temp = null;
if (Read(ref temp))
{
value = temp;
}
else
{
return false;
}
break;
}
default:
throw InvalidProtocolBufferException.InvalidTag();
}
return true;
}
#region ICodedInputStream Members
bool ICodedInputStream.ReadTag(out uint fieldTag, out string fieldName)
{
fieldTag = 0;
if (PeekNext(out fieldName))
{
return true;
}
return false;
}
bool ICodedInputStream.ReadDouble(ref double value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadFloat(ref float value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadUInt64(ref ulong value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadInt64(ref long value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadInt32(ref int value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadFixed64(ref ulong value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadFixed32(ref uint value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadBool(ref bool value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadString(ref string value)
{
return Read(ref value);
}
void ICodedInputStream.ReadGroup(int fieldNumber, IBuilderLite builder, ExtensionRegistry extensionRegistry)
{
if (_depth++ > MaxDepth)
{
throw new RecursionLimitExceededException();
}
ReadGroup(builder, extensionRegistry);
_depth--;
}
void ICodedInputStream.ReadUnknownGroup(int fieldNumber, IBuilderLite builder)
{
throw new NotSupportedException();
}
void ICodedInputStream.ReadMessage(IBuilderLite builder, ExtensionRegistry extensionRegistry)
{
if (_depth++ > MaxDepth)
{
throw new RecursionLimitExceededException();
}
ReadMessage(builder, extensionRegistry);
_depth--;
}
bool ICodedInputStream.ReadBytes(ref ByteString value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadUInt32(ref uint value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadEnum(ref IEnumLite value, out object unknown, IEnumLiteMap mapping)
{
value = null;
unknown = null;
if (ReadEnum(ref unknown))
{
if (unknown is int)
{
value = mapping.FindValueByNumber((int) unknown);
}
else if (unknown is string)
{
value = mapping.FindValueByName((string) unknown);
}
return value != null;
}
return false;
}
bool ICodedInputStream.ReadEnum<T>(ref T value, out object rawValue)
{
rawValue = null;
if (ReadEnum(ref rawValue))
{
if (Enum.IsDefined(typeof(T), rawValue))
{
if (rawValue is int)
{
value = (T) rawValue;
}
else if (rawValue is string)
{
value = (T) Enum.Parse(typeof(T), (string) rawValue, false);
}
else
{
value = default(T);
return false;
}
return true;
}
}
return false;
}
bool ICodedInputStream.ReadSFixed32(ref int value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadSFixed64(ref long value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadSInt32(ref int value)
{
return Read(ref value);
}
bool ICodedInputStream.ReadSInt64(ref long value)
{
return Read(ref value);
}
void ICodedInputStream.ReadPrimitiveArray(FieldType fieldType, uint fieldTag, string fieldName,
ICollection<object> list)
{
ReadArray(fieldType, fieldName, list);
}
void ICodedInputStream.ReadEnumArray(uint fieldTag, string fieldName, ICollection<IEnumLite> list,
out ICollection<object> unknown, IEnumLiteMap mapping)
{
unknown = null;
List<object> array = new List<object>();
if (ReadEnumArray(fieldName, array))
{
foreach (object rawValue in array)
{
IEnumLite item = null;
if (rawValue is int)
{
item = mapping.FindValueByNumber((int) rawValue);
}
else if (rawValue is string)
{
item = mapping.FindValueByName((string) rawValue);
}
if (item != null)
{
list.Add(item);
}
else
{
if (unknown == null)
{
unknown = new List<object>();
}
unknown.Add(rawValue);
}
}
}
}
void ICodedInputStream.ReadEnumArray<T>(uint fieldTag, string fieldName, ICollection<T> list,
out ICollection<object> unknown)
{
unknown = null;
List<object> array = new List<object>();
if (ReadEnumArray(fieldName, array))
{
foreach (object rawValue in array)
{
if (rawValue is int)
{
list.Add((T) rawValue);
}
else if (rawValue is string)
{
list.Add((T) Enum.Parse(typeof(T), (string) rawValue, false));
}
else
{
if (unknown == null)
{
unknown = new List<object>();
}
unknown.Add(rawValue);
}
}
}
}
void ICodedInputStream.ReadMessageArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType,
ExtensionRegistry registry)
{
if (_depth++ > MaxDepth)
{
throw new RecursionLimitExceededException();
}
ReadMessageArray(fieldName, list, messageType, registry);
_depth--;
}
void ICodedInputStream.ReadGroupArray<T>(uint fieldTag, string fieldName, ICollection<T> list, T messageType,
ExtensionRegistry registry)
{
if (_depth++ > MaxDepth)
{
throw new RecursionLimitExceededException();
}
ReadGroupArray(fieldName, list, messageType, registry);
_depth--;
}
bool ICodedInputStream.ReadPrimitiveField(FieldType fieldType, ref object value)
{
return ReadField(fieldType, ref value);
}
bool ICodedInputStream.IsAtEnd
{
get
{
string next;
return PeekNext(out next) == false;
}
}
bool ICodedInputStream.SkipField()
{
Skip();
return true;
}
void ICodedInputStream.ReadStringArray(uint fieldTag, string fieldName, ICollection<string> list)
{
ReadArray(FieldType.String, fieldName, list);
}
void ICodedInputStream.ReadBytesArray(uint fieldTag, string fieldName, ICollection<ByteString> list)
{
ReadArray(FieldType.Bytes, fieldName, list);
}
void ICodedInputStream.ReadBoolArray(uint fieldTag, string fieldName, ICollection<bool> list)
{
ReadArray(FieldType.Bool, fieldName, list);
}
void ICodedInputStream.ReadInt32Array(uint fieldTag, string fieldName, ICollection<int> list)
{
ReadArray(FieldType.Int32, fieldName, list);
}
void ICodedInputStream.ReadSInt32Array(uint fieldTag, string fieldName, ICollection<int> list)
{
ReadArray(FieldType.SInt32, fieldName, list);
}
void ICodedInputStream.ReadUInt32Array(uint fieldTag, string fieldName, ICollection<uint> list)
{
ReadArray(FieldType.UInt32, fieldName, list);
}
void ICodedInputStream.ReadFixed32Array(uint fieldTag, string fieldName, ICollection<uint> list)
{
ReadArray(FieldType.Fixed32, fieldName, list);
}
void ICodedInputStream.ReadSFixed32Array(uint fieldTag, string fieldName, ICollection<int> list)
{
ReadArray(FieldType.SFixed32, fieldName, list);
}
void ICodedInputStream.ReadInt64Array(uint fieldTag, string fieldName, ICollection<long> list)
{
ReadArray(FieldType.Int64, fieldName, list);
}
void ICodedInputStream.ReadSInt64Array(uint fieldTag, string fieldName, ICollection<long> list)
{
ReadArray(FieldType.SInt64, fieldName, list);
}
void ICodedInputStream.ReadUInt64Array(uint fieldTag, string fieldName, ICollection<ulong> list)
{
ReadArray(FieldType.UInt64, fieldName, list);
}
void ICodedInputStream.ReadFixed64Array(uint fieldTag, string fieldName, ICollection<ulong> list)
{
ReadArray(FieldType.Fixed64, fieldName, list);
}
void ICodedInputStream.ReadSFixed64Array(uint fieldTag, string fieldName, ICollection<long> list)
{
ReadArray(FieldType.SFixed64, fieldName, list);
}
void ICodedInputStream.ReadDoubleArray(uint fieldTag, string fieldName, ICollection<double> list)
{
ReadArray(FieldType.Double, fieldName, list);
}
void ICodedInputStream.ReadFloatArray(uint fieldTag, string fieldName, ICollection<float> list)
{
ReadArray(FieldType.Float, fieldName, list);
}
#endregion
internal static bool TryParseInt32(string text, out int number)
{
return TryParseInt32(text, NumberStyles.Any, CultureInfo.InvariantCulture, out number);
}
internal static bool TryParseInt32(string text, NumberStyles style, IFormatProvider format, out int number)
{
#if COMPACT_FRAMEWORK
try
{
number = int.Parse(text, NumberStyles.Integer, CultureInfo.InvariantCulture);
return true;
}
catch
{
number = 0;
return false;
}
#else
return int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out number);
#endif
}
}
}