//
// Copyright (c) ZeroC, Inc. All rights reserved.
//
namespace Ice
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Protocol = IceInternal.Protocol;
///
/// Throws a UserException corresponding to the given Slice type Id, such as "::Module::MyException".
/// If the implementation does not throw an exception, the Ice run time will fall back
/// to using its default behavior for instantiating the user exception.
///
/// A Slice type Id corresponding to a Slice user exception.
public delegate void UserExceptionFactory(string id);
///
/// Interface for input streams used to extract Slice types from a sequence of bytes.
///
public class InputStream
{
///
/// Constructing an InputStream without providing a communicator means the stream will
/// use the default encoding version. A communicator is required in order to unmarshal
/// proxies. You can supply a communicator later by calling initialize().
///
public InputStream()
{
initialize(Util.currentEncoding);
_buf = new IceInternal.Buffer();
}
///
/// Constructing an InputStream without providing a communicator means the stream will
/// use the default encoding version. A communicator is required in order to unmarshal
/// proxies. You can supply a communicator later by calling initialize().
///
/// The byte array containing encoded Slice types.
public InputStream(byte[] data)
{
initialize(Util.currentEncoding);
_buf = new IceInternal.Buffer(data);
}
public InputStream(IceInternal.ByteBuffer buf)
{
initialize(Util.currentEncoding);
_buf = new IceInternal.Buffer(buf);
}
public InputStream(IceInternal.Buffer buf) :
this(buf, false)
{
}
public InputStream(IceInternal.Buffer buf, bool adopt)
{
initialize(Util.currentEncoding);
_buf = new IceInternal.Buffer(buf, adopt);
}
///
/// This constructor uses the communicator's default encoding version.
///
/// The communicator to use when initializing the stream.
public InputStream(Communicator communicator)
{
initialize(communicator);
_buf = new IceInternal.Buffer();
}
///
/// This constructor uses the communicator's default encoding version.
///
/// The communicator to use when initializing the stream.
/// The byte array containing encoded Slice types.
public InputStream(Communicator communicator, byte[] data)
{
initialize(communicator);
_buf = new IceInternal.Buffer(data);
}
public InputStream(Communicator communicator, IceInternal.ByteBuffer buf)
{
initialize(communicator);
_buf = new IceInternal.Buffer(buf);
}
public InputStream(Communicator communicator, IceInternal.Buffer buf) :
this(communicator, buf, false)
{
}
public InputStream(Communicator communicator, IceInternal.Buffer buf, bool adopt)
{
initialize(communicator);
_buf = new IceInternal.Buffer(buf, adopt);
}
///
/// This constructor uses the given encoding version.
///
/// The desired encoding version.
public InputStream(EncodingVersion encoding)
{
initialize(encoding);
_buf = new IceInternal.Buffer();
}
///
/// This constructor uses the given encoding version.
///
/// The desired encoding version.
/// The byte array containing encoded Slice types.
public InputStream(EncodingVersion encoding, byte[] data)
{
initialize(encoding);
_buf = new IceInternal.Buffer(data);
}
public InputStream(EncodingVersion encoding, IceInternal.ByteBuffer buf)
{
initialize(encoding);
_buf = new IceInternal.Buffer(buf);
}
public InputStream(EncodingVersion encoding, IceInternal.Buffer buf) :
this(encoding, buf, false)
{
}
public InputStream(EncodingVersion encoding, IceInternal.Buffer buf, bool adopt)
{
initialize(encoding);
_buf = new IceInternal.Buffer(buf, adopt);
}
///
/// This constructor uses the given encoding version.
///
/// The communicator to use when initializing the stream.
/// The desired encoding version.
public InputStream(Communicator communicator, EncodingVersion encoding)
{
initialize(communicator, encoding);
_buf = new IceInternal.Buffer();
}
///
/// This constructor uses the given encoding version.
///
/// The communicator to use when initializing the stream.
/// The desired encoding version.
/// The byte array containing encoded Slice types.
public InputStream(Communicator communicator, EncodingVersion encoding, byte[] data)
{
initialize(communicator, encoding);
_buf = new IceInternal.Buffer(data);
}
public InputStream(Communicator communicator, EncodingVersion encoding, IceInternal.ByteBuffer buf)
{
initialize(communicator, encoding);
_buf = new IceInternal.Buffer(buf);
}
public InputStream(Communicator communicator, EncodingVersion encoding, IceInternal.Buffer buf) :
this(communicator, encoding, buf, false)
{
}
public InputStream(Communicator communicator, EncodingVersion encoding, IceInternal.Buffer buf, bool adopt)
{
initialize(communicator, encoding);
_buf = new IceInternal.Buffer(buf, adopt);
}
public InputStream(IceInternal.Instance instance, EncodingVersion encoding)
{
initialize(instance, encoding);
_buf = new IceInternal.Buffer();
}
public InputStream(IceInternal.Instance instance, EncodingVersion encoding, byte[] data)
{
initialize(instance, encoding);
_buf = new IceInternal.Buffer(data);
}
public InputStream(IceInternal.Instance instance, EncodingVersion encoding, IceInternal.ByteBuffer buf)
{
initialize(instance, encoding);
_buf = new IceInternal.Buffer(buf);
}
public InputStream(IceInternal.Instance instance, EncodingVersion encoding, IceInternal.Buffer buf) :
this(instance, encoding, buf, false)
{
}
public InputStream(IceInternal.Instance instance, EncodingVersion encoding, IceInternal.Buffer buf, bool adopt)
{
initialize(instance, encoding);
_buf = new IceInternal.Buffer(buf, adopt);
}
///
/// Initializes the stream to use the communicator's default encoding version.
///
/// The communicator to use when initializing the stream.
public void initialize(Communicator communicator)
{
Debug.Assert(communicator != null);
IceInternal.Instance instance = IceInternal.Util.getInstance(communicator);
initialize(instance, instance.defaultsAndOverrides().defaultEncoding);
}
///
/// Initializes the stream to use the given communicator and encoding version.
///
/// The communicator to use when initializing the stream.
/// The desired encoding version.
public void initialize(Communicator communicator, EncodingVersion encoding)
{
Debug.Assert(communicator != null);
IceInternal.Instance instance = IceInternal.Util.getInstance(communicator);
initialize(instance, encoding);
}
private void initialize(IceInternal.Instance instance, EncodingVersion encoding)
{
initialize(encoding);
_instance = instance;
_traceSlicing = _instance.traceLevels().slicing > 0;
_classGraphDepthMax = _instance.classGraphDepthMax();
_valueFactoryManager = _instance.initializationData().valueFactoryManager;
_logger = _instance.initializationData().logger;
_classResolver = _instance.resolveClass;
}
private void initialize(EncodingVersion encoding)
{
_instance = null;
_encoding = encoding;
_encapsStack = null;
_encapsCache = null;
_traceSlicing = false;
_classGraphDepthMax = 0x7fffffff;
_closure = null;
_sliceValues = true;
_startSeq = -1;
_minSeqSize = 0;
}
///
/// Resets this stream. This method allows the stream to be reused, to avoid creating
/// unnecessary garbage.
///
public void reset()
{
_buf.reset();
clear();
}
///
/// Releases any data retained by encapsulations. Internally calls clear().
///
public void clear()
{
if(_encapsStack != null)
{
Debug.Assert(_encapsStack.next == null);
_encapsStack.next = _encapsCache;
_encapsCache = _encapsStack;
_encapsStack = null;
_encapsCache.reset();
}
_startSeq = -1;
_sliceValues = true;
}
///
/// Sets the value factory manager to use when marshaling value instances. If the stream
/// was initialized with a communicator, the communicator's value factory manager will
/// be used by default.
///
/// The value factory manager.
public void setValueFactoryManager(ValueFactoryManager vfm)
{
_valueFactoryManager = vfm;
}
///
/// Sets the logger to use when logging trace messages. If the stream
/// was initialized with a communicator, the communicator's logger will
/// be used by default.
///
/// The logger to use for logging trace messages.
public void setLogger(Logger logger)
{
_logger = logger;
}
///
/// Sets the compact ID resolver to use when unmarshaling value and exception
/// instances. If the stream was initialized with a communicator, the communicator's
/// resolver will be used by default.
///
/// The compact ID resolver.
public void setCompactIdResolver(System.Func r)
{
_compactIdResolver = r;
}
///
/// Sets the class resolver, which the stream will use when attempting to unmarshal
/// a value or exception. If the stream was initialized with a communicator, the communicator's
/// resolver will be used by default.
///
/// The class resolver.
public void setClassResolver(System.Func r)
{
_classResolver = r;
}
///
/// Determines the behavior of the stream when extracting instances of Slice classes.
/// An instance is "sliced" when a factory cannot be found for a Slice type ID.
/// The stream's default behavior is to slice instances.
///
/// If true (the default), slicing is enabled; if false,
/// slicing is disabled. If slicing is disabled and the stream encounters a Slice type ID
/// during decoding for which no value factory is installed, it raises NoValueFactoryException.
///
public void setSliceValues(bool b)
{
_sliceValues = b;
}
///
/// Determines whether the stream logs messages about slicing instances of Slice values.
///
/// True to enable logging, false to disable logging.
public void setTraceSlicing(bool b)
{
_traceSlicing = b;
}
///
/// Set the maximum depth allowed for graph of Slice class instances.
///
/// The maximum depth.
public void setClassGraphDepthMax(int classGraphDepthMax)
{
if(classGraphDepthMax < 1)
{
_classGraphDepthMax = 0x7fffffff;
}
else
{
_classGraphDepthMax = classGraphDepthMax;
}
}
///
/// Retrieves the closure object associated with this stream.
///
/// The closure object.
public object getClosure()
{
return _closure;
}
///
/// Associates a closure object with this stream.
///
/// The new closure object.
/// The previous closure object, or null.
public object setClosure(object p)
{
object prev = _closure;
_closure = p;
return prev;
}
public IceInternal.Instance instance()
{
return _instance;
}
///
/// Swaps the contents of one stream with another.
///
/// The other stream.
public void swap(InputStream other)
{
Debug.Assert(_instance == other._instance);
IceInternal.Buffer tmpBuf = other._buf;
other._buf = _buf;
_buf = tmpBuf;
EncodingVersion tmpEncoding = other._encoding;
other._encoding = _encoding;
_encoding = tmpEncoding;
bool tmpTraceSlicing = other._traceSlicing;
other._traceSlicing = _traceSlicing;
_traceSlicing = tmpTraceSlicing;
object tmpClosure = other._closure;
other._closure = _closure;
_closure = tmpClosure;
bool tmpSliceValues = other._sliceValues;
other._sliceValues = _sliceValues;
_sliceValues = tmpSliceValues;
int tmpClassGraphDepthMax = other._classGraphDepthMax;
other._classGraphDepthMax = _classGraphDepthMax;
_classGraphDepthMax = tmpClassGraphDepthMax;
//
// Swap is never called for InputStreams that have encapsulations being read. However,
// encapsulations might still be set in case un-marshalling failed. We just
// reset the encapsulations if there are still some set.
//
resetEncapsulation();
other.resetEncapsulation();
int tmpStartSeq = other._startSeq;
other._startSeq = _startSeq;
_startSeq = tmpStartSeq;
int tmpMinSeqSize = other._minSeqSize;
other._minSeqSize = _minSeqSize;
_minSeqSize = tmpMinSeqSize;
ValueFactoryManager tmpVfm = other._valueFactoryManager;
other._valueFactoryManager = _valueFactoryManager;
_valueFactoryManager = tmpVfm;
Logger tmpLogger = other._logger;
other._logger = _logger;
_logger = tmpLogger;
System.Func tmpCompactIdResolver = other._compactIdResolver;
other._compactIdResolver = _compactIdResolver;
_compactIdResolver = tmpCompactIdResolver;
System.Func tmpClassResolver = other._classResolver;
other._classResolver = _classResolver;
_classResolver = tmpClassResolver;
}
private void resetEncapsulation()
{
_encapsStack = null;
}
///
/// Resizes the stream to a new size.
///
/// The new size.
public void resize(int sz)
{
_buf.resize(sz, true);
_buf.b.position(sz);
}
public IceInternal.Buffer getBuffer()
{
return _buf;
}
///
/// Marks the start of a class instance.
///
public void startValue()
{
Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
_encapsStack.decoder.startInstance(SliceType.ValueSlice);
}
///
/// Marks the end of a class instance.
///
/// True if unknown slices should be preserved, false otherwise.
/// A SlicedData object containing the preserved slices for unknown types.
public SlicedData endValue(bool preserve)
{
Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
return _encapsStack.decoder.endInstance(preserve);
}
///
/// Marks the start of a user exception.
///
public void startException()
{
Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
_encapsStack.decoder.startInstance(SliceType.ExceptionSlice);
}
///
/// Marks the end of a user exception.
///
/// True if unknown slices should be preserved, false otherwise.
/// A SlicedData object containing the preserved slices for unknown types.
public SlicedData endException(bool preserve)
{
Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
return _encapsStack.decoder.endInstance(preserve);
}
///
/// Reads the start of an encapsulation.
///
/// The encapsulation encoding version.
public EncodingVersion startEncapsulation()
{
Encaps curr = _encapsCache;
if(curr != null)
{
curr.reset();
_encapsCache = _encapsCache.next;
}
else
{
curr = new Encaps();
}
curr.next = _encapsStack;
_encapsStack = curr;
_encapsStack.start = _buf.b.position();
//
// I don't use readSize() for encapsulations, because when creating an encapsulation,
// I must know in advance how many bytes the size information will require in the data
// stream. If I use an Int, it is always 4 bytes. For readSize(), it could be 1 or 5 bytes.
//
int sz = readInt();
if(sz < 6)
{
throw new UnmarshalOutOfBoundsException();
}
if(sz - 4 > _buf.b.remaining())
{
throw new UnmarshalOutOfBoundsException();
}
_encapsStack.sz = sz;
EncodingVersion encoding = new EncodingVersion();
encoding.ice_readMembers(this);
Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported.
_encapsStack.setEncoding(encoding);
return encoding;
}
///
/// Ends the previous encapsulation.
///
public void endEncapsulation()
{
Debug.Assert(_encapsStack != null);
if(!_encapsStack.encoding_1_0)
{
skipOptionals();
if(_buf.b.position() != _encapsStack.start + _encapsStack.sz)
{
throw new EncapsulationException();
}
}
else if(_buf.b.position() != _encapsStack.start + _encapsStack.sz)
{
if(_buf.b.position() + 1 != _encapsStack.start + _encapsStack.sz)
{
throw new EncapsulationException();
}
//
// Ice version < 3.3 had a bug where user exceptions with
// class members could be encoded with a trailing byte
// when dispatched with AMD. So we tolerate an extra byte
// in the encapsulation.
//
try
{
_buf.b.get();
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
Encaps curr = _encapsStack;
_encapsStack = curr.next;
curr.next = _encapsCache;
_encapsCache = curr;
_encapsCache.reset();
}
///
/// Skips an empty encapsulation.
///
/// The encapsulation's encoding version.
public EncodingVersion skipEmptyEncapsulation()
{
int sz = readInt();
if(sz < 6)
{
throw new EncapsulationException();
}
if(sz - 4 > _buf.b.remaining())
{
throw new UnmarshalOutOfBoundsException();
}
var encoding = new EncodingVersion();
encoding.ice_readMembers(this);
Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported.
if(encoding.Equals(Util.Encoding_1_0))
{
if(sz != 6)
{
throw new EncapsulationException();
}
}
else
{
// Skip the optional content of the encapsulation if we are expecting an
// empty encapsulation.
_buf.b.position(_buf.b.position() + sz - 6);
}
return encoding;
}
///
/// Returns a blob of bytes representing an encapsulation. The encapsulation's encoding version
/// is returned in the argument.
///
/// The encapsulation's encoding version.
/// The encoded encapsulation.
public byte[] readEncapsulation(out EncodingVersion encoding)
{
int sz = readInt();
if(sz < 6)
{
throw new UnmarshalOutOfBoundsException();
}
if(sz - 4 > _buf.b.remaining())
{
throw new UnmarshalOutOfBoundsException();
}
encoding = new EncodingVersion();
encoding.ice_readMembers(this);
_buf.b.position(_buf.b.position() - 6);
byte[] v = new byte[sz];
try
{
_buf.b.get(v);
return v;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Determines the current encoding version.
///
/// The encoding version.
public EncodingVersion getEncoding()
{
return _encapsStack != null ? _encapsStack.encoding : _encoding;
}
///
/// Determines the size of the current encapsulation, excluding the encapsulation header.
///
/// The size of the encapsulated data.
public int getEncapsulationSize()
{
Debug.Assert(_encapsStack != null);
return _encapsStack.sz - 6;
}
///
/// Skips over an encapsulation.
///
/// The encoding version of the skipped encapsulation.
public EncodingVersion skipEncapsulation()
{
int sz = readInt();
if(sz < 6)
{
throw new UnmarshalOutOfBoundsException();
}
EncodingVersion encoding = new EncodingVersion();
encoding.ice_readMembers(this);
try
{
_buf.b.position(_buf.b.position() + sz - 6);
}
catch(ArgumentOutOfRangeException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
return encoding;
}
///
/// Reads the start of a class instance or exception slice.
///
/// The Slice type ID for this slice.
public string startSlice() // Returns type ID of next slice
{
Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
return _encapsStack.decoder.startSlice();
}
///
/// Indicates that the end of a class instance or exception slice has been reached.
///
public void endSlice()
{
Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
_encapsStack.decoder.endSlice();
}
///
/// Skips over a class instance or exception slice.
///
public void skipSlice()
{
Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
_encapsStack.decoder.skipSlice();
}
///
/// Indicates that unmarshaling is complete, except for any class instances. The application must call this
/// method only if the stream actually contains class instances. Calling readPendingValues triggers the
/// calls to the System.Action delegates to inform the application that unmarshaling of an instance
/// is complete.
///
public void readPendingValues()
{
if(_encapsStack != null && _encapsStack.decoder != null)
{
_encapsStack.decoder.readPendingValues();
}
else if(_encapsStack != null ? _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0))
{
//
// If using the 1.0 encoding and no instances were read, we
// still read an empty sequence of pending instances if
// requested (i.e.: if this is called).
//
// This is required by the 1.0 encoding, even if no instances
// are written we do marshal an empty sequence if marshaled
// data types use classes.
//
skipSize();
}
}
///
/// Extracts a size from the stream.
///
/// The extracted size.
public int readSize()
{
try
{
byte b = _buf.b.get();
if(b == 255)
{
int v = _buf.b.getInt();
if(v < 0)
{
throw new UnmarshalOutOfBoundsException();
}
return v;
}
else
{
return b; // byte is unsigned
}
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Reads and validates a sequence size.
///
/// The extracted size.
public int readAndCheckSeqSize(int minSize)
{
int sz = readSize();
if(sz == 0)
{
return 0;
}
//
// The _startSeq variable points to the start of the sequence for which
// we expect to read at least _minSeqSize bytes from the stream.
//
// If not initialized or if we already read more data than _minSeqSize,
// we reset _startSeq and _minSeqSize for this sequence (possibly a
// top-level sequence or enclosed sequence it doesn't really matter).
//
// Otherwise, we are reading an enclosed sequence and we have to bump
// _minSeqSize by the minimum size that this sequence will require on
// the stream.
//
// The goal of this check is to ensure that when we start un-marshalling
// a new sequence, we check the minimal size of this new sequence against
// the estimated remaining buffer size. This estimatation is based on
// the minimum size of the enclosing sequences, it's _minSeqSize.
//
if(_startSeq == -1 || _buf.b.position() > (_startSeq + _minSeqSize))
{
_startSeq = _buf.b.position();
_minSeqSize = sz * minSize;
}
else
{
_minSeqSize += sz * minSize;
}
//
// If there isn't enough data to read on the stream for the sequence (and
// possibly enclosed sequences), something is wrong with the marshalled
// data: it's claiming having more data that what is possible to read.
//
if(_startSeq + _minSeqSize > _buf.size())
{
throw new UnmarshalOutOfBoundsException();
}
return sz;
}
///
/// Reads a blob of bytes from the stream. The length of the given array determines how many bytes are read.
///
/// Bytes from the stream.
public void readBlob(byte[] v)
{
try
{
_buf.b.get(v);
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Reads a blob of bytes from the stream.
///
/// The number of bytes to read.
/// The requested bytes as a byte array.
public byte[] readBlob(int sz)
{
if(_buf.b.remaining() < sz)
{
throw new UnmarshalOutOfBoundsException();
}
byte[] v = new byte[sz];
try
{
_buf.b.get(v);
return v;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Determine if an optional value is available for reading.
///
/// The tag associated with the value.
/// The optional format for the value.
/// True if the value is present, false otherwise.
public bool readOptional(int tag, OptionalFormat expectedFormat)
{
Debug.Assert(_encapsStack != null);
if(_encapsStack.decoder != null)
{
return _encapsStack.decoder.readOptional(tag, expectedFormat);
}
else
{
return readOptImpl(tag, expectedFormat);
}
}
///
/// Extracts a byte value from the stream.
///
/// The extracted byte.
public byte readByte()
{
try
{
return _buf.b.get();
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts an optional byte value from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readByte(int tag)
{
if(readOptional(tag, OptionalFormat.F1))
{
return new Optional(readByte());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional byte value from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readByte(int tag, out bool isset, out byte v)
{
if(isset = readOptional(tag, OptionalFormat.F1))
{
v = readByte();
}
else
{
v = 0;
}
}
///
/// Extracts a sequence of byte values from the stream.
///
/// The extracted byte sequence.
public byte[] readByteSeq()
{
try
{
int sz = readAndCheckSeqSize(1);
byte[] v = new byte[sz];
_buf.b.get(v);
return v;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of byte values from the stream.
///
/// The extracted byte sequence as a list.
public void readByteSeq(out List l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new List(readByteSeq());
}
///
/// Extracts a sequence of byte values from the stream.
///
/// The extracted byte sequence as a linked list.
public void readByteSeq(out LinkedList l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new LinkedList(readByteSeq());
}
///
/// Extracts a sequence of byte values from the stream.
///
/// The extracted byte sequence as a queue.
public void readByteSeq(out Queue l)
{
//
// Reading into an array and copy-constructing the
// queue is faster than constructing the queue
// and adding to it one element at a time.
//
l = new Queue(readByteSeq());
}
///
/// Extracts a sequence of byte values from the stream.
///
/// The extracted byte sequence as a stack.
public void readByteSeq(out Stack l)
{
//
// Reverse the contents by copying into an array first
// because the stack is marshaled in top-to-bottom order.
//
byte[] array = readByteSeq();
Array.Reverse(array);
l = new Stack(array);
}
///
/// Extracts an optional byte sequence from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readByteSeq(int tag)
{
if(readOptional(tag, OptionalFormat.VSize))
{
return new Optional(readByteSeq());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional byte sequence from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readByteSeq(int tag, out bool isset, out byte[] v)
{
if(isset = readOptional(tag, OptionalFormat.VSize))
{
v = readByteSeq();
}
else
{
v = null;
}
}
///
/// Extracts a serializable object from the stream.
///
/// The serializable object.
public object readSerializable()
{
int sz = readAndCheckSeqSize(1);
if(sz == 0)
{
return null;
}
try
{
var f = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.All, _instance));
#pragma warning disable SYSLIB0011 // Type or member is obsolete
return f.Deserialize(new IceInternal.InputStreamWrapper(sz, this));
#pragma warning restore SYSLIB0011 // Type or member is obsolete
}
catch(System.Exception ex)
{
throw new MarshalException("cannot deserialize object:", ex);
}
}
///
/// Extracts a boolean value from the stream.
///
/// The extracted boolean.
public bool readBool()
{
try
{
return _buf.b.get() == 1;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts an optional boolean value from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readBool(int tag)
{
if(readOptional(tag, OptionalFormat.F1))
{
return new Optional(readBool());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional boolean value from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readBool(int tag, out bool isset, out bool v)
{
if(isset = readOptional(tag, OptionalFormat.F1))
{
v = readBool();
}
else
{
v = false;
}
}
///
/// Extracts a sequence of boolean values from the stream.
///
/// The extracted boolean sequence.
public bool[] readBoolSeq()
{
try
{
int sz = readAndCheckSeqSize(1);
bool[] v = new bool[sz];
_buf.b.getBoolSeq(v);
return v;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of boolean values from the stream.
///
/// The extracted boolean sequence as a list.
public void readBoolSeq(out List l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new List(readBoolSeq());
}
///
/// Extracts a sequence of boolean values from the stream.
///
/// The extracted boolean sequence as a linked list.
public void readBoolSeq(out LinkedList l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new LinkedList(readBoolSeq());
}
///
/// Extracts a sequence of boolean values from the stream.
///
/// The extracted boolean sequence as a queue.
public void readBoolSeq(out Queue l)
{
//
// Reading into an array and copy-constructing the
// queue is faster than constructing the queue
// and adding to it one element at a time.
//
l = new Queue(readBoolSeq());
}
///
/// Extracts a sequence of boolean values from the stream.
///
/// The extracted boolean sequence as a stack.
public void readBoolSeq(out Stack l)
{
//
// Reverse the contents by copying into an array first
// because the stack is marshaled in top-to-bottom order.
//
bool[] array = readBoolSeq();
Array.Reverse(array);
l = new Stack(array);
}
///
/// Extracts an optional boolean sequence from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readBoolSeq(int tag)
{
if(readOptional(tag, OptionalFormat.VSize))
{
return new Optional(readBoolSeq());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional boolean sequence from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readBoolSeq(int tag, out bool isset, out bool[] v)
{
if(isset = readOptional(tag, OptionalFormat.VSize))
{
v = readBoolSeq();
}
else
{
v = null;
}
}
///
/// Extracts a short value from the stream.
///
/// The extracted short.
public short readShort()
{
try
{
return _buf.b.getShort();
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts an optional short value from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readShort(int tag)
{
if(readOptional(tag, OptionalFormat.F2))
{
return new Optional(readShort());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional short value from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readShort(int tag, out bool isset, out short v)
{
if(isset = readOptional(tag, OptionalFormat.F2))
{
v = readShort();
}
else
{
v = 0;
}
}
///
/// Extracts a sequence of short values from the stream.
///
/// The extracted short sequence.
public short[] readShortSeq()
{
try
{
int sz = readAndCheckSeqSize(2);
short[] v = new short[sz];
_buf.b.getShortSeq(v);
return v;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of short values from the stream.
///
/// The extracted short sequence as a list.
public void readShortSeq(out List l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new List(readShortSeq());
}
///
/// Extracts a sequence of short values from the stream.
///
/// The extracted short sequence as a linked list.
public void readShortSeq(out LinkedList l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new LinkedList(readShortSeq());
}
///
/// Extracts a sequence of short values from the stream.
///
/// The extracted short sequence as a queue.
public void readShortSeq(out Queue l)
{
//
// Reading into an array and copy-constructing the
// queue is faster than constructing the queue
// and adding to it one element at a time.
//
l = new Queue(readShortSeq());
}
///
/// Extracts a sequence of short values from the stream.
///
/// The extracted short sequence as a stack.
public void readShortSeq(out Stack l)
{
//
// Reverse the contents by copying into an array first
// because the stack is marshaled in top-to-bottom order.
//
short[] array = readShortSeq();
Array.Reverse(array);
l = new Stack(array);
}
///
/// Extracts an optional short sequence from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readShortSeq(int tag)
{
if(readOptional(tag, OptionalFormat.VSize))
{
skipSize();
return new Optional(readShortSeq());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional short sequence from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readShortSeq(int tag, out bool isset, out short[] v)
{
if(isset = readOptional(tag, OptionalFormat.VSize))
{
skipSize();
v = readShortSeq();
}
else
{
v = null;
}
}
///
/// Extracts an int value from the stream.
///
/// The extracted int.
public int readInt()
{
try
{
return _buf.b.getInt();
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts an optional int value from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readInt(int tag)
{
if(readOptional(tag, OptionalFormat.F4))
{
return new Optional(readInt());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional int value from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readInt(int tag, out bool isset, out int v)
{
if(isset = readOptional(tag, OptionalFormat.F4))
{
v = readInt();
}
else
{
v = 0;
}
}
///
/// Extracts a sequence of int values from the stream.
///
/// The extracted int sequence.
public int[] readIntSeq()
{
try
{
int sz = readAndCheckSeqSize(4);
int[] v = new int[sz];
_buf.b.getIntSeq(v);
return v;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of int values from the stream.
///
/// The extracted int sequence as a list.
public void readIntSeq(out List l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new List(readIntSeq());
}
///
/// Extracts a sequence of int values from the stream.
///
/// The extracted int sequence as a linked list.
public void readIntSeq(out LinkedList l)
{
try
{
int sz = readAndCheckSeqSize(4);
l = new LinkedList();
for(int i = 0; i < sz; ++i)
{
l.AddLast(_buf.b.getInt());
}
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of int values from the stream.
///
/// The extracted int sequence as a queue.
public void readIntSeq(out Queue l)
{
//
// Reading into an array and copy-constructing the
// queue takes the same time as constructing the queue
// and adding to it one element at a time, so
// we avoid the copy.
//
try
{
int sz = readAndCheckSeqSize(4);
l = new Queue(sz);
for(int i = 0; i < sz; ++i)
{
l.Enqueue(_buf.b.getInt());
}
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of int values from the stream.
///
/// The extracted int sequence as a stack.
public void readIntSeq(out Stack l)
{
//
// Reverse the contents by copying into an array first
// because the stack is marshaled in top-to-bottom order.
//
int[] array = readIntSeq();
Array.Reverse(array);
l = new Stack(array);
}
///
/// Extracts an optional int sequence from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readIntSeq(int tag)
{
if(readOptional(tag, OptionalFormat.VSize))
{
skipSize();
return new Optional(readIntSeq());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional int sequence from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readIntSeq(int tag, out bool isset, out int[] v)
{
if(isset = readOptional(tag, OptionalFormat.VSize))
{
skipSize();
v = readIntSeq();
}
else
{
v = null;
}
}
///
/// Extracts a long value from the stream.
///
/// The extracted long.
public long readLong()
{
try
{
return _buf.b.getLong();
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts an optional long value from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readLong(int tag)
{
if(readOptional(tag, OptionalFormat.F8))
{
return new Optional(readLong());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional long value from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readLong(int tag, out bool isset, out long v)
{
if(isset = readOptional(tag, OptionalFormat.F8))
{
v = readLong();
}
else
{
v = 0;
}
}
///
/// Extracts a sequence of long values from the stream.
///
/// The extracted long sequence.
public long[] readLongSeq()
{
try
{
int sz = readAndCheckSeqSize(8);
long[] v = new long[sz];
_buf.b.getLongSeq(v);
return v;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of long values from the stream.
///
/// The extracted long sequence as a list.
public void readLongSeq(out List l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new List(readLongSeq());
}
///
/// Extracts a sequence of long values from the stream.
///
/// The extracted long sequence as a linked list.
public void readLongSeq(out LinkedList l)
{
try
{
int sz = readAndCheckSeqSize(4);
l = new LinkedList();
for(int i = 0; i < sz; ++i)
{
l.AddLast(_buf.b.getLong());
}
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of long values from the stream.
///
/// The extracted long sequence as a queue.
public void readLongSeq(out Queue l)
{
//
// Reading into an array and copy-constructing the
// queue takes the same time as constructing the queue
// and adding to it one element at a time, so
// we avoid the copy.
//
try
{
int sz = readAndCheckSeqSize(4);
l = new Queue(sz);
for(int i = 0; i < sz; ++i)
{
l.Enqueue(_buf.b.getLong());
}
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of long values from the stream.
///
/// The extracted long sequence as a stack.
public void readLongSeq(out Stack l)
{
//
// Reverse the contents by copying into an array first
// because the stack is marshaled in top-to-bottom order.
//
long[] array = readLongSeq();
Array.Reverse(array);
l = new Stack(array);
}
///
/// Extracts an optional long sequence from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readLongSeq(int tag)
{
if(readOptional(tag, OptionalFormat.VSize))
{
skipSize();
return new Optional(readLongSeq());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional long sequence from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readLongSeq(int tag, out bool isset, out long[] v)
{
if(isset = readOptional(tag, OptionalFormat.VSize))
{
skipSize();
v = readLongSeq();
}
else
{
v = null;
}
}
///
/// Extracts a float value from the stream.
///
/// The extracted float.
public float readFloat()
{
try
{
return _buf.b.getFloat();
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts an optional float value from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readFloat(int tag)
{
if(readOptional(tag, OptionalFormat.F4))
{
return new Optional(readFloat());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional float value from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readFloat(int tag, out bool isset, out float v)
{
if(isset = readOptional(tag, OptionalFormat.F4))
{
v = readFloat();
}
else
{
v = 0;
}
}
///
/// Extracts a sequence of float values from the stream.
///
/// The extracted float sequence.
public float[] readFloatSeq()
{
try
{
int sz = readAndCheckSeqSize(4);
float[] v = new float[sz];
_buf.b.getFloatSeq(v);
return v;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of float values from the stream.
///
/// The extracted float sequence as a list.
public void readFloatSeq(out List l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new List(readFloatSeq());
}
///
/// Extracts a sequence of float values from the stream.
///
/// The extracted float sequence as a linked list.
public void readFloatSeq(out LinkedList l)
{
try
{
int sz = readAndCheckSeqSize(4);
l = new LinkedList();
for(int i = 0; i < sz; ++i)
{
l.AddLast(_buf.b.getFloat());
}
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of float values from the stream.
///
/// The extracted float sequence as a queue.
public void readFloatSeq(out Queue l)
{
//
// Reading into an array and copy-constructing the
// queue takes the same time as constructing the queue
// and adding to it one element at a time, so
// we avoid the copy.
//
try
{
int sz = readAndCheckSeqSize(4);
l = new Queue(sz);
for(int i = 0; i < sz; ++i)
{
l.Enqueue(_buf.b.getFloat());
}
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of float values from the stream.
///
/// The extracted float sequence as a stack.
public void readFloatSeq(out Stack l)
{
//
// Reverse the contents by copying into an array first
// because the stack is marshaled in top-to-bottom order.
//
float[] array = readFloatSeq();
Array.Reverse(array);
l = new Stack(array);
}
///
/// Extracts an optional float sequence from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readFloatSeq(int tag)
{
if(readOptional(tag, OptionalFormat.VSize))
{
skipSize();
return new Optional(readFloatSeq());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional float sequence from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readFloatSeq(int tag, out bool isset, out float[] v)
{
if(isset = readOptional(tag, OptionalFormat.VSize))
{
skipSize();
v = readFloatSeq();
}
else
{
v = null;
}
}
///
/// Extracts a double value from the stream.
///
/// The extracted double.
public double readDouble()
{
try
{
return _buf.b.getDouble();
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts an optional double value from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readDouble(int tag)
{
if(readOptional(tag, OptionalFormat.F8))
{
return new Optional(readDouble());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional double value from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readDouble(int tag, out bool isset, out double v)
{
if(isset = readOptional(tag, OptionalFormat.F8))
{
v = readDouble();
}
else
{
v = 0;
}
}
///
/// Extracts a sequence of double values from the stream.
///
/// The extracted double sequence.
public double[] readDoubleSeq()
{
try
{
int sz = readAndCheckSeqSize(8);
double[] v = new double[sz];
_buf.b.getDoubleSeq(v);
return v;
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of double values from the stream.
///
/// The extracted double sequence as a list.
public void readDoubleSeq(out List l)
{
//
// Reading into an array and copy-constructing the
// list is faster than constructing the list
// and adding to it one element at a time.
//
l = new List(readDoubleSeq());
}
///
/// Extracts a sequence of double values from the stream.
///
/// The extracted double sequence as a linked list.
public void readDoubleSeq(out LinkedList l)
{
try
{
int sz = readAndCheckSeqSize(4);
l = new LinkedList();
for(int i = 0; i < sz; ++i)
{
l.AddLast(_buf.b.getDouble());
}
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of double values from the stream.
///
/// The extracted double sequence as a queue.
public void readDoubleSeq(out Queue l)
{
//
// Reading into an array and copy-constructing the
// queue takes the same time as constructing the queue
// and adding to it one element at a time, so
// we avoid the copy.
//
try
{
int sz = readAndCheckSeqSize(4);
l = new Queue(sz);
for(int i = 0; i < sz; ++i)
{
l.Enqueue(_buf.b.getDouble());
}
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
}
///
/// Extracts a sequence of double values from the stream.
///
/// The extracted double sequence as a stack.
public void readDoubleSeq(out Stack l)
{
//
// Reverse the contents by copying into an array first
// because the stack is marshaled in top-to-bottom order.
//
double[] array = readDoubleSeq();
Array.Reverse(array);
l = new Stack(array);
}
///
/// Extracts an optional double sequence from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readDoubleSeq(int tag)
{
if(readOptional(tag, OptionalFormat.VSize))
{
skipSize();
return new Optional(readDoubleSeq());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional double sequence from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readDoubleSeq(int tag, out bool isset, out double[] v)
{
if(isset = readOptional(tag, OptionalFormat.VSize))
{
skipSize();
v = readDoubleSeq();
}
else
{
v = null;
}
}
private static System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(false, true);
///
/// Extracts a string from the stream.
///
/// The extracted string.
public string readString()
{
int len = readSize();
if(len == 0)
{
return "";
}
//
// Check the buffer has enough bytes to read.
//
if(_buf.b.remaining() < len)
{
throw new UnmarshalOutOfBoundsException();
}
try
{
//
// We reuse the _stringBytes array to avoid creating
// excessive garbage
//
if(_stringBytes == null || len > _stringBytes.Length)
{
_stringBytes = new byte[len];
}
_buf.b.get(_stringBytes, 0, len);
return utf8.GetString(_stringBytes, 0, len);
}
catch(InvalidOperationException ex)
{
throw new UnmarshalOutOfBoundsException(ex);
}
catch(ArgumentException ex)
{
throw new MarshalException("Invalid UTF8 string", ex);
}
}
///
/// Extracts an optional string from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readString(int tag)
{
if(readOptional(tag, OptionalFormat.VSize))
{
return new Optional(readString());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional string from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readString(int tag, out bool isset, out string v)
{
if(isset = readOptional(tag, OptionalFormat.VSize))
{
v = readString();
}
else
{
v = null;
}
}
///
/// Extracts a sequence of strings from the stream.
///
/// The extracted string sequence.
public string[] readStringSeq()
{
int sz = readAndCheckSeqSize(1);
string[] v = new string[sz];
for(int i = 0; i < sz; i++)
{
v[i] = readString();
}
return v;
}
///
/// Extracts a sequence of strings from the stream.
///
/// The extracted string sequence as a list.
public void readStringSeq(out List l)
{
//
// Reading into an array and copy-constructing the
// list is slower than constructing the list
// and adding to it one element at a time.
//
int sz = readAndCheckSeqSize(1);
l = new List(sz);
for(int i = 0; i < sz; ++i)
{
l.Add(readString());
}
}
///
/// Extracts a sequence of strings from the stream.
///
/// The extracted string sequence as a linked list.
public void readStringSeq(out LinkedList l)
{
//
// Reading into an array and copy-constructing the
// list is slower than constructing the list
// and adding to it one element at a time.
//
int sz = readAndCheckSeqSize(1);
l = new LinkedList();
for(int i = 0; i < sz; ++i)
{
l.AddLast(readString());
}
}
///
/// Extracts a sequence of strings from the stream.
///
/// The extracted string sequence as a queue.
public void readStringSeq(out Queue l)
{
//
// Reading into an array and copy-constructing the
// queue is slower than constructing the queue
// and adding to it one element at a time.
//
int sz = readAndCheckSeqSize(1);
l = new Queue();
for(int i = 0; i < sz; ++i)
{
l.Enqueue(readString());
}
}
///
/// Extracts a sequence of strings from the stream.
///
/// The extracted string sequence as a stack.
public void readStringSeq(out Stack l)
{
//
// Reverse the contents by copying into an array first
// because the stack is marshaled in top-to-bottom order.
//
string[] array = readStringSeq();
Array.Reverse(array);
l = new Stack(array);
}
///
/// Extracts an optional string sequence from the stream.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readStringSeq(int tag)
{
if(readOptional(tag, OptionalFormat.FSize))
{
skip(4);
return new Optional(readStringSeq());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional string sequence from the stream.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readStringSeq(int tag, out bool isset, out string[] v)
{
if(isset = readOptional(tag, OptionalFormat.FSize))
{
skip(4);
v = readStringSeq();
}
else
{
v = null;
}
}
///
/// Extracts a proxy from the stream. The stream must have been initialized with a communicator.
///
/// The extracted proxy.
public ObjectPrx readProxy()
{
return _instance.proxyFactory().streamToProxy(this);
}
///
/// Extracts an optional proxy from the stream. The stream must have been initialized with a communicator.
///
/// The numeric tag associated with the value.
/// The optional value.
public Optional readProxy(int tag)
{
if(readOptional(tag, OptionalFormat.FSize))
{
skip(4);
return new Optional(readProxy());
}
else
{
return new Optional();
}
}
///
/// Extracts an optional proxy from the stream. The stream must have been initialized with a communicator.
///
/// The numeric tag associated with the value.
/// True if the optional value is present, false otherwise.
/// The optional value.
public void readProxy(int tag, out bool isset, out ObjectPrx v)
{
if(isset = readOptional(tag, OptionalFormat.FSize))
{
skip(4);
v = readProxy();
}
else
{
v = null;
}
}
///
/// Read an enumerated value.
///
/// The maximum enumerator value in the definition.
/// The enumerator.
public int readEnum(int maxValue)
{
if(getEncoding().Equals(Util.Encoding_1_0))
{
if(maxValue < 127)
{
return readByte();
}
else if(maxValue < 32767)
{
return readShort();
}
else
{
return readInt();
}
}
else
{
return readSize();
}
}
///
/// Extracts the index of a Slice value from the stream.
///
/// The callback to notify the application when the extracted instance is available.
/// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
/// corresponding instance has been fully unmarshaled.
public void readValue(System.Action cb) where T : Value
{
readValue(v => {
if(v == null || v is T)
{
cb((T)v);
}
else
{
IceInternal.Ex.throwUOE(typeof(T), v);
}
});
}
///
/// Extracts the index of a Slice value from the stream.
///
/// The callback to notify the application when the extracted instance is available.
/// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
/// corresponding instance has been fully unmarshaled.
public void readValue(System.Action cb)
{
initEncaps();
_encapsStack.decoder.readValue(cb);
}
///
/// Extracts the index of an optional Slice value from the stream.
///
/// The numeric tag associated with the value.
/// The callback to notify the application when the extracted instance is available (if any).
/// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
/// corresponding instance has been fully unmarshaled.
public void readValue(int tag, System.Action cb) where T : Value
{
readValue(tag, v => {
if(v == null || v is T)
{
cb((T)v);
}
else
{
IceInternal.Ex.throwUOE(typeof(T), v);
}
});
}
///
/// Extracts the index of an optional Slice value from the stream.
///
/// The numeric tag associated with the value.
/// The callback to notify the application when the extracted instance is available (if any).
/// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
/// corresponding instance has been fully unmarshaled.
public void readValue(int tag, System.Action cb)
{
if(readOptional(tag, OptionalFormat.Class))
{
readValue(cb);
}
}
///
/// Extracts a user exception from the stream and throws it.
///
public void throwException()
{
throwException(null);
}
///
/// Extracts a user exception from the stream and throws it.
///
/// The user exception factory, or null to use the stream's default behavior.
public void throwException(UserExceptionFactory factory)
{
initEncaps();
_encapsStack.decoder.throwException(factory);
}
///
/// Skip the given number of bytes.
///
/// The number of bytes to skip
public void skip(int size)
{
if(size < 0 || size > _buf.b.remaining())
{
throw new UnmarshalOutOfBoundsException();
}
_buf.b.position(_buf.b.position() + size);
}
///
/// Skip over a size value.
///
public void skipSize()
{
byte b = readByte();
if(b == 255)
{
skip(4);
}
}
///
/// Determines the current position in the stream.
///
/// The current position.
public int pos()
{
return _buf.b.position();
}
///
/// Sets the current position in the stream.
///
/// The new position.
public void pos(int n)
{
_buf.b.position(n);
}
///
/// Determines the current size of the stream.
///
/// The current size.
public int size()
{
return _buf.size();
}
///
/// Determines whether the stream is empty.
///
/// True if the internal buffer has no data, false otherwise.
public bool isEmpty()
{
return _buf.empty();
}
private bool readOptImpl(int readTag, OptionalFormat expectedFormat)
{
if(isEncoding_1_0())
{
return false; // Optional members aren't supported with the 1.0 encoding.
}
while(true)
{
if(_buf.b.position() >= _encapsStack.start + _encapsStack.sz)
{
return false; // End of encapsulation also indicates end of optionals.
}
int v = readByte();
if(v == Protocol.OPTIONAL_END_MARKER)
{
_buf.b.position(_buf.b.position() - 1); // Rewind.
return false;
}
OptionalFormat format = (OptionalFormat)(v & 0x07); // First 3 bits.
int tag = v >> 3;
if(tag == 30)
{
tag = readSize();
}
if(tag > readTag)
{
int offset = tag < 30 ? 1 : (tag < 255 ? 2 : 6); // Rewind
_buf.b.position(_buf.b.position() - offset);
return false; // No optional data members with the requested tag.
}
else if(tag < readTag)
{
skipOptional(format); // Skip optional data members
}
else
{
if(format != expectedFormat)
{
throw new MarshalException("invalid optional data member `" + tag + "': unexpected format");
}
return true;
}
}
}
private void skipOptional(OptionalFormat format)
{
switch(format)
{
case OptionalFormat.F1:
{
skip(1);
break;
}
case OptionalFormat.F2:
{
skip(2);
break;
}
case OptionalFormat.F4:
{
skip(4);
break;
}
case OptionalFormat.F8:
{
skip(8);
break;
}
case OptionalFormat.Size:
{
skipSize();
break;
}
case OptionalFormat.VSize:
{
skip(readSize());
break;
}
case OptionalFormat.FSize:
{
skip(readInt());
break;
}
case OptionalFormat.Class:
{
readValue(null);
break;
}
}
}
private bool skipOptionals()
{
//
// Skip remaining un-read optional members.
//
while(true)
{
if(_buf.b.position() >= _encapsStack.start + _encapsStack.sz)
{
return false; // End of encapsulation also indicates end of optionals.
}
int v = readByte();
if(v == Protocol.OPTIONAL_END_MARKER)
{
return true;
}
OptionalFormat format = (OptionalFormat)(v & 0x07); // Read first 3 bits.
if((v >> 3) == 30)
{
skipSize();
}
skipOptional(format);
}
}
private UserException createUserException(string id)
{
UserException userEx = null;
try
{
if(_classResolver != null)
{
Type c = _classResolver(id);
if(c != null)
{
Debug.Assert(!c.IsAbstract && !c.IsInterface);
userEx = (UserException)IceInternal.AssemblyUtil.createInstance(c);
}
}
}
catch(Exception ex)
{
throw new MarshalException(ex);
}
return userEx;
}
private IceInternal.Instance _instance;
private IceInternal.Buffer _buf;
private object _closure;
private byte[] _stringBytes; // Reusable array for reading strings.
private enum SliceType { NoSlice, ValueSlice, ExceptionSlice }
abstract private class EncapsDecoder
{
protected struct PatchEntry
{
public PatchEntry(System.Action cb, int classGraphDepth)
{
this.cb = cb;
this.classGraphDepth = classGraphDepth;
}
public System.Action cb;
public int classGraphDepth;
};
internal EncapsDecoder(InputStream stream, Encaps encaps, bool sliceValues,
int classGraphDepthMax, ValueFactoryManager f,
System.Func cr)
{
_stream = stream;
_encaps = encaps;
_sliceValues = sliceValues;
_classGraphDepthMax = classGraphDepthMax;
_classGraphDepth = 0;
_valueFactoryManager = f;
_classResolver = cr;
_typeIdIndex = 0;
_unmarshaledMap = new Dictionary();
}
internal abstract void readValue(System.Action cb);
internal abstract void throwException(UserExceptionFactory factory);
internal abstract void startInstance(SliceType type);
internal abstract SlicedData endInstance(bool preserve);
internal abstract string startSlice();
internal abstract void endSlice();
internal abstract void skipSlice();
internal virtual bool readOptional(int tag, OptionalFormat format)
{
return false;
}
internal virtual void readPendingValues()
{
}
protected string readTypeId(bool isIndex)
{
if(_typeIdMap == null)
{
_typeIdMap = new Dictionary();
}
if(isIndex)
{
int index = _stream.readSize();
string typeId;
if(!_typeIdMap.TryGetValue(index, out typeId))
{
throw new UnmarshalOutOfBoundsException();
}
return typeId;
}
else
{
string typeId = _stream.readString();
_typeIdMap.Add(++_typeIdIndex, typeId);
return typeId;
}
}
protected Type resolveClass(string typeId)
{
Type cls = null;
if(_typeIdCache == null)
{
_typeIdCache = new Dictionary(); // Lazy initialization.
}
else
{
_typeIdCache.TryGetValue(typeId, out cls);
}
if(cls == typeof(EncapsDecoder)) // Marker for non-existent class.
{
cls = null;
}
else if(cls == null)
{
try
{
if(_classResolver != null)
{
cls = _classResolver(typeId);
_typeIdCache.Add(typeId, cls != null ? cls : typeof(EncapsDecoder));
}
}
catch(Exception ex)
{
throw new NoValueFactoryException("no value factory", typeId, ex);
}
}
return cls;
}
protected Value newInstance(string typeId)
{
//
// Try to find a factory registered for the specific type.
//
var userFactory = _valueFactoryManager.find(typeId);
Value v = null;
if(userFactory != null)
{
v = userFactory(typeId);
}
//
// If that fails, invoke the default factory if one has been
// registered.
//
if(v == null)
{
userFactory = _valueFactoryManager.find("");
if(userFactory != null)
{
v = userFactory(typeId);
}
}
//
// Last chance: try to instantiate the class dynamically.
//
if(v == null)
{
Type cls = resolveClass(typeId);
if(cls != null)
{
try
{
Debug.Assert(!cls.IsAbstract && !cls.IsInterface);
v = (Value)IceInternal.AssemblyUtil.createInstance(cls);
}
catch(Exception ex)
{
throw new NoValueFactoryException("no value factory", typeId, ex);
}
}
}
return v;
}
protected void addPatchEntry(int index, System.Action cb)
{
Debug.Assert(index > 0);
//
// Check if we already unmarshaled the instance. If that's the case,
// just call the callback and we're done.
//
Value obj;
if(_unmarshaledMap.TryGetValue(index, out obj))
{
cb(obj);
return;
}
if(_patchMap == null)
{
_patchMap = new Dictionary>();
}
//
// Add patch entry if the instance isn't unmarshaled yet,
// the callback will be called when the instance is
// unmarshaled.
//
LinkedList l;
if(!_patchMap.TryGetValue(index, out l))
{
//
// We have no outstanding instances to be patched for this
// index, so make a new entry in the patch map.
//
l = new LinkedList();
_patchMap.Add(index, l);
}
//
// Append a patch entry for this instance.
//
l.AddLast(new PatchEntry(cb, _classGraphDepth));
}
protected void unmarshal(int index, Value v)
{
//
// Add the instance to the map of unmarshaled instances, this must
// be done before reading the instances (for circular references).
//
_unmarshaledMap.Add(index, v);
//
// Read the instance.
//
v.iceRead(_stream);
if(_patchMap != null)
{
//
// Patch all instances now that the instance is unmarshaled.
//
LinkedList l;
if(_patchMap.TryGetValue(index, out l))
{
Debug.Assert(l.Count > 0);
//
// Patch all pointers that refer to the instance.
//
foreach(PatchEntry entry in l)
{
entry.cb(v);
}
//
// Clear out the patch map for that index -- there is nothing left
// to patch for that index for the time being.
//
_patchMap.Remove(index);
}
}
if((_patchMap == null || _patchMap.Count == 0) && _valueList == null)
{
try
{
v.ice_postUnmarshal();
}
catch(System.Exception ex)
{
string s = "exception raised by ice_postUnmarshal:\n" + ex;
_stream.instance().initializationData().logger.warning(s);
}
}
else
{
if(_valueList == null)
{
_valueList = new List();
}
_valueList.Add(v);
if(_patchMap == null || _patchMap.Count == 0)
{
//
// Iterate over the instance list and invoke ice_postUnmarshal on
// each instance. We must do this after all instances have been
// unmarshaled in order to ensure that any instance data members
// have been properly patched.
//
foreach(var p in _valueList)
{
try
{
p.ice_postUnmarshal();
}
catch(System.Exception ex)
{
string s = "exception raised by ice_postUnmarshal:\n" + ex;
_stream.instance().initializationData().logger.warning(s);
}
}
_valueList.Clear();
}
}
}
protected readonly InputStream _stream;
protected readonly Encaps _encaps;
protected readonly bool _sliceValues;
protected readonly int _classGraphDepthMax;
protected int _classGraphDepth;
protected ValueFactoryManager _valueFactoryManager;
protected System.Func _classResolver;
//
// Encapsulation attributes for object unmarshaling.
//
protected Dictionary> _patchMap;
private Dictionary _unmarshaledMap;
private Dictionary _typeIdMap;
private int _typeIdIndex;
private List _valueList;
private Dictionary _typeIdCache;
}
private sealed class EncapsDecoder10 : EncapsDecoder
{
internal EncapsDecoder10(InputStream stream, Encaps encaps, bool sliceValues, int classGraphDepthMax,
ValueFactoryManager f, System.Func cr)
: base(stream, encaps, sliceValues, classGraphDepthMax, f, cr)
{
_sliceType = SliceType.NoSlice;
}
internal override void readValue(System.Action cb)
{
Debug.Assert(cb != null);
//
// Object references are encoded as a negative integer in 1.0.
//
int index = _stream.readInt();
if(index > 0)
{
throw new MarshalException("invalid object id");
}
index = -index;
if(index == 0)
{
cb(null);
}
else
{
addPatchEntry(index, cb);
}
}
internal override void throwException(UserExceptionFactory factory)
{
Debug.Assert(_sliceType == SliceType.NoSlice);
//
// User exception with the 1.0 encoding start with a bool flag
// that indicates whether or not the exception has classes.
//
// This allows reading the pending instances even if some part of
// the exception was sliced.
//
bool usesClasses = _stream.readBool();
_sliceType = SliceType.ExceptionSlice;
_skipFirstSlice = false;
//
// Read the first slice header.
//
startSlice();
string mostDerivedId = _typeId;
while(true)
{
UserException userEx = null;
//
// Use a factory if one was provided.
//
if(factory != null)
{
try
{
factory(_typeId);
}
catch(UserException ex)
{
userEx = ex;
}
}
if(userEx == null)
{
userEx = _stream.createUserException(_typeId);
}
//
// We found the exception.
//
if(userEx != null)
{
userEx.iceRead(_stream);
if(usesClasses)
{
readPendingValues();
}
throw userEx;
// Never reached.
}
//
// Slice off what we don't understand.
//
skipSlice();
try
{
startSlice();
}
catch(UnmarshalOutOfBoundsException ex)
{
//
// An oversight in the 1.0 encoding means there is no marker to indicate
// the last slice of an exception. As a result, we just try to read the
// next type ID, which raises UnmarshalOutOfBoundsException when the
// input buffer underflows.
//
// Set the reason member to a more helpful message.
//
ex.reason = "unknown exception type `" + mostDerivedId + "'";
throw;
}
}
}
internal override void startInstance(SliceType sliceType)
{
Debug.Assert(_sliceType == sliceType);
_skipFirstSlice = true;
}
internal override SlicedData endInstance(bool preserve)
{
//
// Read the Ice::Object slice.
//
if(_sliceType == SliceType.ValueSlice)
{
startSlice();
int sz = _stream.readSize(); // For compatibility with the old AFM.
if(sz != 0)
{
throw new MarshalException("invalid Object slice");
}
endSlice();
}
_sliceType = SliceType.NoSlice;
return null;
}
internal override string startSlice()
{
//
// If first slice, don't read the header, it was already read in
// readInstance or throwException to find the factory.
//
if(_skipFirstSlice)
{
_skipFirstSlice = false;
return _typeId;
}
//
// For instances, first read the type ID bool which indicates
// whether or not the type ID is encoded as a string or as an
// index. For exceptions, the type ID is always encoded as a
// string.
//
if(_sliceType == SliceType.ValueSlice) // For exceptions, the type ID is always encoded as a string
{
bool isIndex = _stream.readBool();
_typeId = readTypeId(isIndex);
}
else
{
_typeId = _stream.readString();
}
_sliceSize = _stream.readInt();
if(_sliceSize < 4)
{
throw new UnmarshalOutOfBoundsException();
}
return _typeId;
}
internal override void endSlice()
{
}
internal override void skipSlice()
{
if(_stream.instance().traceLevels().slicing > 0)
{
Logger logger = _stream.instance().initializationData().logger;
string slicingCat = _stream.instance().traceLevels().slicingCat;
if(_sliceType == SliceType.ValueSlice)
{
IceInternal.TraceUtil.traceSlicing("object", _typeId, slicingCat, logger);
}
else
{
IceInternal.TraceUtil.traceSlicing("exception", _typeId, slicingCat, logger);
}
}
Debug.Assert(_sliceSize >= 4);
_stream.skip(_sliceSize - 4);
}
internal override void readPendingValues()
{
int num;
do
{
num = _stream.readSize();
for(int k = num; k > 0; --k)
{
readInstance();
}
}
while(num > 0);
if(_patchMap != null && _patchMap.Count > 0)
{
//
// If any entries remain in the patch map, the sender has sent an index for an instance, but failed
// to supply the instance.
//
throw new MarshalException("index for class received, but no instance");
}
}
private void readInstance()
{
int index = _stream.readInt();
if(index <= 0)
{
throw new MarshalException("invalid object id");
}
_sliceType = SliceType.ValueSlice;
_skipFirstSlice = false;
//
// Read the first slice header.
//
startSlice();
string mostDerivedId = _typeId;
Value v = null;
while(true)
{
//
// For the 1.0 encoding, the type ID for the base Object class
// marks the last slice.
//
if(_typeId.Equals(Value.ice_staticId()))
{
throw new NoValueFactoryException("", mostDerivedId);
}
v = newInstance(_typeId);
//
// We found a factory, we get out of this loop.
//
if(v != null)
{
break;
}
//
// If slicing is disabled, stop unmarshaling.
//
if(!_sliceValues)
{
throw new NoValueFactoryException("no value factory found and slicing is disabled", _typeId);
}
//
// Slice off what we don't understand.
//
skipSlice();
startSlice(); // Read next Slice header for next iteration.
}
//
// Compute the biggest class graph depth of this object. To compute this,
// we get the class graph depth of each ancestor from the patch map and
// keep the biggest one.
//
_classGraphDepth = 0;
LinkedList l;
if(_patchMap != null && _patchMap.TryGetValue(index, out l))
{
Debug.Assert(l.Count > 0);
foreach(PatchEntry entry in l)
{
if(entry.classGraphDepth > _classGraphDepth)
{
_classGraphDepth = entry.classGraphDepth;
}
}
}
if(++_classGraphDepth > _classGraphDepthMax)
{
throw new MarshalException("maximum class graph depth reached");
}
//
// Unmarshal the instance and add it to the map of unmarshaled instances.
//
unmarshal(index, v);
}
// Object/exception attributes
private SliceType _sliceType;
private bool _skipFirstSlice;
// Slice attributes
private int _sliceSize;
private string _typeId;
}
private sealed class EncapsDecoder11 : EncapsDecoder
{
internal EncapsDecoder11(InputStream stream, Encaps encaps, bool sliceValues, int classGraphDepthMax,
ValueFactoryManager f, System.Func cr, System.Func r)
: base(stream, encaps, sliceValues, classGraphDepthMax, f, cr)
{
_compactIdResolver = r;
_current = null;
_valueIdIndex = 1;
}
internal override void readValue(System.Action cb)
{
int index = _stream.readSize();
if(index < 0)
{
throw new MarshalException("invalid object id");
}
else if(index == 0)
{
if(cb != null)
{
cb(null);
}
}
else if(_current != null && (_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
{
//
// When reading an instance within a slice and there's an
// indirect instance table, always read an indirect reference
// that points to an instance from the indirect instance table
// marshaled at the end of the Slice.
//
// Maintain a list of indirect references. Note that the
// indirect index starts at 1, so we decrement it by one to
// derive an index into the indirection table that we'll read
// at the end of the slice.
//
if(cb != null)
{
if(_current.indirectPatchList == null)
{
_current.indirectPatchList = new Stack();
}
IndirectPatchEntry e = new IndirectPatchEntry();
e.index = index - 1;
e.patcher = cb;
_current.indirectPatchList.Push(e);
}
}
else
{
readInstance(index, cb);
}
}
internal override void throwException(UserExceptionFactory factory)
{
Debug.Assert(_current == null);
push(SliceType.ExceptionSlice);
//
// Read the first slice header.
//
startSlice();
string mostDerivedId = _current.typeId;
while(true)
{
UserException userEx = null;
//
// Use a factory if one was provided.
//
if(factory != null)
{
try
{
factory(_current.typeId);
}
catch(UserException ex)
{
userEx = ex;
}
}
if(userEx == null)
{
userEx = _stream.createUserException(_current.typeId);
}
//
// We found the exception.
//
if(userEx != null)
{
userEx.iceRead(_stream);
throw userEx;
// Never reached.
}
//
// Slice off what we don't understand.
//
skipSlice();
if((_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0)
{
if(mostDerivedId.StartsWith("::", StringComparison.Ordinal))
{
throw new UnknownUserException(mostDerivedId.Substring(2));
}
else
{
throw new UnknownUserException(mostDerivedId);
}
}
startSlice();
}
}
internal override void startInstance(SliceType sliceType)
{
Debug.Assert(_current.sliceType == sliceType);
_current.skipFirstSlice = true;
}
internal override SlicedData endInstance(bool preserve)
{
SlicedData slicedData = null;
if(preserve)
{
slicedData = readSlicedData();
}
if(_current.slices != null)
{
_current.slices.Clear();
_current.indirectionTables.Clear();
}
_current = _current.previous;
return slicedData;
}
internal override string startSlice()
{
//
// If first slice, don't read the header, it was already read in
// readInstance or throwException to find the factory.
//
if(_current.skipFirstSlice)
{
_current.skipFirstSlice = false;
return _current.typeId;
}
_current.sliceFlags = _stream.readByte();
//
// Read the type ID, for instance slices the type ID is encoded as a
// string or as an index, for exceptions it's always encoded as a
// string.
//
if(_current.sliceType == SliceType.ValueSlice)
{
//
// Must be checked first!
//
if((_current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_COMPACT) == Protocol.FLAG_HAS_TYPE_ID_COMPACT)
{
_current.typeId = "";
_current.compactId = _stream.readSize();
}
else if((_current.sliceFlags &
(Protocol.FLAG_HAS_TYPE_ID_INDEX | Protocol.FLAG_HAS_TYPE_ID_STRING)) != 0)
{
_current.typeId = readTypeId((_current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_INDEX) != 0);
_current.compactId = -1;
}
else
{
// Only the most derived slice encodes the type ID for the compact format.
_current.typeId = "";
_current.compactId = -1;
}
}
else
{
_current.typeId = _stream.readString();
_current.compactId = -1;
}
//
// Read the slice size if necessary.
//
if((_current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
{
_current.sliceSize = _stream.readInt();
if(_current.sliceSize < 4)
{
throw new UnmarshalOutOfBoundsException();
}
}
else
{
_current.sliceSize = 0;
}
return _current.typeId;
}
internal override void endSlice()
{
if((_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0)
{
_stream.skipOptionals();
}
//
// Read the indirection table if one is present and transform the
// indirect patch list into patch entries with direct references.
//
if((_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
{
//
// The table is written as a sequence to conserve space.
//
int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)];
for(int i = 0; i < indirectionTable.Length; ++i)
{
indirectionTable[i] = readInstance(_stream.readSize(), null);
}
//
// Sanity checks. If there are optional members, it's possible
// that not all instance references were read if they are from
// unknown optional data members.
//
if(indirectionTable.Length == 0)
{
throw new MarshalException("empty indirection table");
}
if((_current.indirectPatchList == null || _current.indirectPatchList.Count == 0) &&
(_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) == 0)
{
throw new MarshalException("no references to indirection table");
}
//
// Convert indirect references into direct references.
//
if(_current.indirectPatchList != null)
{
foreach(IndirectPatchEntry e in _current.indirectPatchList)
{
Debug.Assert(e.index >= 0);
if(e.index >= indirectionTable.Length)
{
throw new MarshalException("indirection out of range");
}
addPatchEntry(indirectionTable[e.index], e.patcher);
}
_current.indirectPatchList.Clear();
}
}
}
internal override void skipSlice()
{
if(_stream.instance().traceLevels().slicing > 0)
{
Logger logger = _stream.instance().initializationData().logger;
string slicingCat = _stream.instance().traceLevels().slicingCat;
if(_current.sliceType == SliceType.ExceptionSlice)
{
IceInternal.TraceUtil.traceSlicing("exception", _current.typeId, slicingCat, logger);
}
else
{
IceInternal.TraceUtil.traceSlicing("object", _current.typeId, slicingCat, logger);
}
}
int start = _stream.pos();
if((_current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
{
Debug.Assert(_current.sliceSize >= 4);
_stream.skip(_current.sliceSize - 4);
}
else
{
if(_current.sliceType == SliceType.ValueSlice)
{
throw new NoValueFactoryException("no value factory found and compact format prevents " +
"slicing (the sender should use the sliced format " +
"instead)", _current.typeId);
}
else
{
if(_current.typeId.StartsWith("::", StringComparison.Ordinal))
{
throw new UnknownUserException(_current.typeId.Substring(2));
}
else
{
throw new UnknownUserException(_current.typeId);
}
}
}
//
// Preserve this slice.
//
SliceInfo info = new SliceInfo();
info.typeId = _current.typeId;
info.compactId = _current.compactId;
info.hasOptionalMembers = (_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0;
info.isLastSlice = (_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0;
IceInternal.ByteBuffer b = _stream.getBuffer().b;
int end = b.position();
int dataEnd = end;
if(info.hasOptionalMembers)
{
//
// Don't include the optional member end marker. It will be re-written by
// endSlice when the sliced data is re-written.
//
--dataEnd;
}
info.bytes = new byte[dataEnd - start];
b.position(start);
b.get(info.bytes);
b.position(end);
if(_current.slices == null)
{
_current.slices = new List();
_current.indirectionTables = new List();
}
//
// Read the indirect instance table. We read the instances or their
// IDs if the instance is a reference to an already unmarshaled
// instance.
//
if((_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
{
int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)];
for(int i = 0; i < indirectionTable.Length; ++i)
{
indirectionTable[i] = readInstance(_stream.readSize(), null);
}
_current.indirectionTables.Add(indirectionTable);
}
else
{
_current.indirectionTables.Add(null);
}
_current.slices.Add(info);
}
internal override bool readOptional(int readTag, OptionalFormat expectedFormat)
{
if(_current == null)
{
return _stream.readOptImpl(readTag, expectedFormat);
}
else if((_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0)
{
return _stream.readOptImpl(readTag, expectedFormat);
}
return false;
}
private int readInstance(int index, System.Action cb)
{
Debug.Assert(index > 0);
if(index > 1)
{
if(cb != null)
{
addPatchEntry(index, cb);
}
return index;
}
push(SliceType.ValueSlice);
//
// Get the instance ID before we start reading slices. If some
// slices are skipped, the indirect instance table are still read and
// might read other instances.
//
index = ++_valueIdIndex;
//
// Read the first slice header.
//
startSlice();
string mostDerivedId = _current.typeId;
Value v = null;
while(true)
{
bool updateCache = false;
if(_current.compactId >= 0)
{
updateCache = true;
//
// Translate a compact (numeric) type ID into a class.
//
if(_compactIdCache == null)
{
_compactIdCache = new Dictionary(); // Lazy initialization.
}
else
{
//
// Check the cache to see if we've already translated the compact type ID into a class.
//
Type cls = null;
_compactIdCache.TryGetValue(_current.compactId, out cls);
if(cls != null)
{
try
{
Debug.Assert(!cls.IsAbstract && !cls.IsInterface);
v = (Value)IceInternal.AssemblyUtil.createInstance(cls);
updateCache = false;
}
catch(Exception ex)
{
throw new NoValueFactoryException("no value factory", "compact ID " +
_current.compactId, ex);
}
}
}
//
// If we haven't already cached a class for the compact ID, then try to translate the
// compact ID into a type ID.
//
if(v == null)
{
_current.typeId = "";
if(_compactIdResolver != null)
{
try
{
_current.typeId = _compactIdResolver(_current.compactId);
}
catch(LocalException)
{
throw;
}
catch(System.Exception ex)
{
throw new MarshalException("exception in CompactIdResolver for ID " +
_current.compactId, ex);
}
}
if(_current.typeId.Length == 0)
{
_current.typeId = _stream.instance().resolveCompactId(_current.compactId);
}
}
}
if(v == null && _current.typeId.Length > 0)
{
v = newInstance(_current.typeId);
}
if(v != null)
{
if(updateCache)
{
Debug.Assert(_current.compactId >= 0);
_compactIdCache.Add(_current.compactId, v.GetType());
}
//
// We have an instance, get out of this loop.
//
break;
}
//
// If slicing is disabled, stop unmarshaling.
//
if(!_sliceValues)
{
throw new NoValueFactoryException("no value factory found and slicing is disabled",
_current.typeId);
}
//
// Slice off what we don't understand.
//
skipSlice();
//
// If this is the last slice, keep the instance as an opaque
// UnknownSlicedValue object.
//
if((_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0)
{
//
// Provide a factory with an opportunity to supply the instance.
// We pass the "::Ice::Object" ID to indicate that this is the
// last chance to preserve the instance.
//
v = newInstance(Value.ice_staticId());
if(v == null)
{
v = new UnknownSlicedValue(mostDerivedId);
}
break;
}
startSlice(); // Read next Slice header for next iteration.
}
if(++_classGraphDepth > _classGraphDepthMax)
{
throw new MarshalException("maximum class graph depth reached");
}
//
// Unmarshal the instance.
//
unmarshal(index, v);
--_classGraphDepth;
if(_current == null && _patchMap != null && _patchMap.Count > 0)
{
//
// If any entries remain in the patch map, the sender has sent an index for an instance, but failed
// to supply the instance.
//
throw new MarshalException("index for class received, but no instance");
}
if(cb != null)
{
cb(v);
}
return index;
}
private SlicedData readSlicedData()
{
if(_current.slices == null) // No preserved slices.
{
return null;
}
//
// The _indirectionTables member holds the indirection table for each slice
// in _slices.
//
Debug.Assert(_current.slices.Count == _current.indirectionTables.Count);
for(int n = 0; n < _current.slices.Count; ++n)
{
//
// We use the "instances" list in SliceInfo to hold references
// to the target instances. Note that the instances might not have
// been read yet in the case of a circular reference to an
// enclosing instance.
//
int[] table = _current.indirectionTables[n];
SliceInfo info = _current.slices[n];
info.instances = new Value[table != null ? table.Length : 0];
for(int j = 0; j < info.instances.Length; ++j)
{
var cj = j;
addPatchEntry(table[j], (Ice.Value v) => info.instances[cj] = v);
}
}
return new SlicedData(_current.slices.ToArray());
}
private void push(SliceType sliceType)
{
if(_current == null)
{
_current = new InstanceData(null);
}
else
{
_current = _current.next == null ? new InstanceData(_current) : _current.next;
}
_current.sliceType = sliceType;
_current.skipFirstSlice = false;
}
private sealed class IndirectPatchEntry
{
public int index;
public System.Action patcher;
}
private sealed class InstanceData
{
internal InstanceData(InstanceData previous)
{
if(previous != null)
{
previous.next = this;
}
this.previous = previous;
this.next = null;
}
// Instance attributes
internal SliceType sliceType;
internal bool skipFirstSlice;
internal List slices; // Preserved slices.
internal List indirectionTables;
// Slice attributes
internal byte sliceFlags;
internal int sliceSize;
internal string typeId;
internal int compactId;
internal Stack indirectPatchList;
internal InstanceData previous;
internal InstanceData next;
}
private System.Func _compactIdResolver;
private InstanceData _current;
private int _valueIdIndex; // The ID of the next instance to unmarshal.
private Dictionary _compactIdCache;
}
private sealed class Encaps
{
internal void reset()
{
decoder = null;
}
internal void setEncoding(EncodingVersion encoding)
{
this.encoding = encoding;
encoding_1_0 = encoding.Equals(Util.Encoding_1_0);
}
internal int start;
internal int sz;
internal EncodingVersion encoding;
internal bool encoding_1_0;
internal EncapsDecoder decoder;
internal Encaps next;
}
//
// The encoding version to use when there's no encapsulation to
// read from. This is for example used to read message headers.
//
private EncodingVersion _encoding;
private bool isEncoding_1_0()
{
return _encapsStack != null ? _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0);
}
private Encaps _encapsStack;
private Encaps _encapsCache;
private void initEncaps()
{
if(_encapsStack == null) // Lazy initialization
{
_encapsStack = _encapsCache;
if(_encapsStack != null)
{
_encapsCache = _encapsCache.next;
}
else
{
_encapsStack = new Encaps();
}
_encapsStack.setEncoding(_encoding);
_encapsStack.sz = _buf.b.limit();
}
if(_encapsStack.decoder == null) // Lazy initialization.
{
if(_encapsStack.encoding_1_0)
{
_encapsStack.decoder = new EncapsDecoder10(this, _encapsStack, _sliceValues, _classGraphDepthMax,
_valueFactoryManager, _classResolver);
}
else
{
_encapsStack.decoder = new EncapsDecoder11(this, _encapsStack, _sliceValues, _classGraphDepthMax,
_valueFactoryManager, _classResolver, _compactIdResolver);
}
}
}
private bool _sliceValues;
private bool _traceSlicing;
private int _classGraphDepthMax;
private int _startSeq;
private int _minSeqSize;
private ValueFactoryManager _valueFactoryManager;
private Logger _logger;
private System.Func _compactIdResolver;
private System.Func _classResolver;
}
///
/// Base class for extracting class instances from an input stream.
///
public abstract class ValueReader : Value
{
///
/// Read the instance's data members.
///
/// The input stream to read from.
public abstract void read(InputStream inStream);
public override void iceWrite(OutputStream os)
{
Debug.Assert(false);
}
public override void iceRead(InputStream istr)
{
read(istr);
}
}
}