1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 namespace Ice
6 {
7 
8     using System;
9     using System.Collections.Generic;
10     using System.Diagnostics;
11     using System.Runtime.Serialization;
12     using System.Runtime.Serialization.Formatters.Binary;
13     using Protocol = IceInternal.Protocol;
14 
15     /// <summary>
16     /// Throws a UserException corresponding to the given Slice type Id, such as "::Module::MyException".
17     /// If the implementation does not throw an exception, the Ice run time will fall back
18     /// to using its default behavior for instantiating the user exception.
19     /// </summary>
20     /// <param name="id">A Slice type Id corresponding to a Slice user exception.</param>
UserExceptionFactory(string id)21     public delegate void UserExceptionFactory(string id);
22 
23     /// <summary>
24     /// Interface for input streams used to extract Slice types from a sequence of bytes.
25     /// </summary>
26     public class InputStream
27     {
28 
29         /// <summary>
30         /// Constructing an InputStream without providing a communicator means the stream will
31         /// use the default encoding version. A communicator is required in order to unmarshal
32         /// proxies. You can supply a communicator later by calling initialize().
33         /// </summary>
InputStream()34         public InputStream()
35         {
36             initialize(Util.currentEncoding);
37             _buf = new IceInternal.Buffer();
38         }
39 
40         /// <summary>
41         /// Constructing an InputStream without providing a communicator means the stream will
42         /// use the default encoding version. A communicator is required in order to unmarshal
43         /// proxies. You can supply a communicator later by calling initialize().
44         /// </summary>
45         /// <param name="data">The byte array containing encoded Slice types.</param>
InputStream(byte[] data)46         public InputStream(byte[] data)
47         {
48             initialize(Util.currentEncoding);
49             _buf = new IceInternal.Buffer(data);
50         }
51 
InputStream(IceInternal.ByteBuffer buf)52         public InputStream(IceInternal.ByteBuffer buf)
53         {
54             initialize(Util.currentEncoding);
55             _buf = new IceInternal.Buffer(buf);
56         }
57 
InputStream(IceInternal.Buffer buf)58         public InputStream(IceInternal.Buffer buf) :
59             this(buf, false)
60         {
61         }
62 
InputStream(IceInternal.Buffer buf, bool adopt)63         public InputStream(IceInternal.Buffer buf, bool adopt)
64         {
65             initialize(Util.currentEncoding);
66             _buf = new IceInternal.Buffer(buf, adopt);
67         }
68 
69         /// <summary>
70         /// This constructor uses the communicator's default encoding version.
71         /// </summary>
72         /// <param name="communicator">The communicator to use when initializing the stream.</param>
InputStream(Communicator communicator)73         public InputStream(Communicator communicator)
74         {
75             initialize(communicator);
76             _buf = new IceInternal.Buffer();
77         }
78 
79         /// <summary>
80         /// This constructor uses the communicator's default encoding version.
81         /// </summary>
82         /// <param name="communicator">The communicator to use when initializing the stream.</param>
83         /// <param name="data">The byte array containing encoded Slice types.</param>
InputStream(Communicator communicator, byte[] data)84         public InputStream(Communicator communicator, byte[] data)
85         {
86             initialize(communicator);
87             _buf = new IceInternal.Buffer(data);
88         }
89 
InputStream(Communicator communicator, IceInternal.ByteBuffer buf)90         public InputStream(Communicator communicator, IceInternal.ByteBuffer buf)
91         {
92             initialize(communicator);
93             _buf = new IceInternal.Buffer(buf);
94         }
95 
InputStream(Communicator communicator, IceInternal.Buffer buf)96         public InputStream(Communicator communicator, IceInternal.Buffer buf) :
97             this(communicator, buf, false)
98         {
99         }
100 
InputStream(Communicator communicator, IceInternal.Buffer buf, bool adopt)101         public InputStream(Communicator communicator, IceInternal.Buffer buf, bool adopt)
102         {
103             initialize(communicator);
104             _buf = new IceInternal.Buffer(buf, adopt);
105         }
106 
107         /// <summary>
108         /// This constructor uses the given encoding version.
109         /// </summary>
110         /// <param name="encoding">The desired encoding version.</param>
InputStream(EncodingVersion encoding)111         public InputStream(EncodingVersion encoding)
112         {
113             initialize(encoding);
114             _buf = new IceInternal.Buffer();
115         }
116 
117         /// <summary>
118         /// This constructor uses the given encoding version.
119         /// </summary>
120         /// <param name="encoding">The desired encoding version.</param>
121         /// <param name="data">The byte array containing encoded Slice types.</param>
InputStream(EncodingVersion encoding, byte[] data)122         public InputStream(EncodingVersion encoding, byte[] data)
123         {
124             initialize(encoding);
125             _buf = new IceInternal.Buffer(data);
126         }
127 
InputStream(EncodingVersion encoding, IceInternal.ByteBuffer buf)128         public InputStream(EncodingVersion encoding, IceInternal.ByteBuffer buf)
129         {
130             initialize(encoding);
131             _buf = new IceInternal.Buffer(buf);
132         }
133 
InputStream(EncodingVersion encoding, IceInternal.Buffer buf)134         public InputStream(EncodingVersion encoding, IceInternal.Buffer buf) :
135             this(encoding, buf, false)
136         {
137         }
138 
InputStream(EncodingVersion encoding, IceInternal.Buffer buf, bool adopt)139         public InputStream(EncodingVersion encoding, IceInternal.Buffer buf, bool adopt)
140         {
141             initialize(encoding);
142             _buf = new IceInternal.Buffer(buf, adopt);
143         }
144 
145         /// <summary>
146         /// This constructor uses the given encoding version.
147         /// </summary>
148         /// <param name="communicator">The communicator to use when initializing the stream.</param>
149         /// <param name="encoding">The desired encoding version.</param>
InputStream(Communicator communicator, EncodingVersion encoding)150         public InputStream(Communicator communicator, EncodingVersion encoding)
151         {
152             initialize(communicator, encoding);
153             _buf = new IceInternal.Buffer();
154         }
155 
156         /// <summary>
157         /// This constructor uses the given encoding version.
158         /// </summary>
159         /// <param name="communicator">The communicator to use when initializing the stream.</param>
160         /// <param name="encoding">The desired encoding version.</param>
161         /// <param name="data">The byte array containing encoded Slice types.</param>
InputStream(Communicator communicator, EncodingVersion encoding, byte[] data)162         public InputStream(Communicator communicator, EncodingVersion encoding, byte[] data)
163         {
164             initialize(communicator, encoding);
165             _buf = new IceInternal.Buffer(data);
166         }
167 
InputStream(Communicator communicator, EncodingVersion encoding, IceInternal.ByteBuffer buf)168         public InputStream(Communicator communicator, EncodingVersion encoding, IceInternal.ByteBuffer buf)
169         {
170             initialize(communicator, encoding);
171             _buf = new IceInternal.Buffer(buf);
172         }
173 
InputStream(Communicator communicator, EncodingVersion encoding, IceInternal.Buffer buf)174         public InputStream(Communicator communicator, EncodingVersion encoding, IceInternal.Buffer buf) :
175             this(communicator, encoding, buf, false)
176         {
177         }
178 
InputStream(Communicator communicator, EncodingVersion encoding, IceInternal.Buffer buf, bool adopt)179         public InputStream(Communicator communicator, EncodingVersion encoding, IceInternal.Buffer buf, bool adopt)
180         {
181             initialize(communicator, encoding);
182             _buf = new IceInternal.Buffer(buf, adopt);
183         }
184 
InputStream(IceInternal.Instance instance, EncodingVersion encoding)185         public InputStream(IceInternal.Instance instance, EncodingVersion encoding)
186         {
187             initialize(instance, encoding);
188             _buf = new IceInternal.Buffer();
189         }
190 
InputStream(IceInternal.Instance instance, EncodingVersion encoding, byte[] data)191         public InputStream(IceInternal.Instance instance, EncodingVersion encoding, byte[] data)
192         {
193             initialize(instance, encoding);
194             _buf = new IceInternal.Buffer(data);
195         }
196 
InputStream(IceInternal.Instance instance, EncodingVersion encoding, IceInternal.ByteBuffer buf)197         public InputStream(IceInternal.Instance instance, EncodingVersion encoding, IceInternal.ByteBuffer buf)
198         {
199             initialize(instance, encoding);
200             _buf = new IceInternal.Buffer(buf);
201         }
202 
InputStream(IceInternal.Instance instance, EncodingVersion encoding, IceInternal.Buffer buf)203         public InputStream(IceInternal.Instance instance, EncodingVersion encoding, IceInternal.Buffer buf) :
204             this(instance, encoding, buf, false)
205         {
206         }
207 
InputStream(IceInternal.Instance instance, EncodingVersion encoding, IceInternal.Buffer buf, bool adopt)208         public InputStream(IceInternal.Instance instance, EncodingVersion encoding, IceInternal.Buffer buf, bool adopt)
209         {
210             initialize(instance, encoding);
211             _buf = new IceInternal.Buffer(buf, adopt);
212         }
213 
214         /// <summary>
215         /// Initializes the stream to use the communicator's default encoding version.
216         /// </summary>
217         /// <param name="communicator">The communicator to use when initializing the stream.</param>
initialize(Communicator communicator)218         public void initialize(Communicator communicator)
219         {
220             Debug.Assert(communicator != null);
221             IceInternal.Instance instance = IceInternal.Util.getInstance(communicator);
222             initialize(instance, instance.defaultsAndOverrides().defaultEncoding);
223         }
224 
225         /// <summary>
226         /// Initializes the stream to use the given communicator and encoding version.
227         /// </summary>
228         /// <param name="communicator">The communicator to use when initializing the stream.</param>
229         /// <param name="encoding">The desired encoding version.</param>
initialize(Communicator communicator, EncodingVersion encoding)230         public void initialize(Communicator communicator, EncodingVersion encoding)
231         {
232             Debug.Assert(communicator != null);
233             IceInternal.Instance instance = IceInternal.Util.getInstance(communicator);
234             initialize(instance, encoding);
235         }
236 
initialize(IceInternal.Instance instance, EncodingVersion encoding)237         private void initialize(IceInternal.Instance instance, EncodingVersion encoding)
238         {
239             initialize(encoding);
240 
241             _instance = instance;
242             _traceSlicing = _instance.traceLevels().slicing > 0;
243             _classGraphDepthMax = _instance.classGraphDepthMax();
244             _valueFactoryManager = _instance.initializationData().valueFactoryManager;
245             _logger = _instance.initializationData().logger;
246             _classResolver = _instance.resolveClass;
247         }
248 
initialize(EncodingVersion encoding)249         private void initialize(EncodingVersion encoding)
250         {
251             _instance = null;
252             _encoding = encoding;
253             _encapsStack = null;
254             _encapsCache = null;
255             _traceSlicing = false;
256             _classGraphDepthMax = 0x7fffffff;
257             _closure = null;
258             _sliceValues = true;
259             _startSeq = -1;
260             _minSeqSize = 0;
261         }
262 
263         /// <summary>
264         /// Resets this stream. This method allows the stream to be reused, to avoid creating
265         /// unnecessary garbage.
266         /// </summary>
reset()267         public void reset()
268         {
269             _buf.reset();
270             clear();
271         }
272 
273         /// <summary>
274         /// Releases any data retained by encapsulations. Internally calls clear().
275         /// </summary>
clear()276         public void clear()
277         {
278             if(_encapsStack != null)
279             {
280                 Debug.Assert(_encapsStack.next == null);
281                 _encapsStack.next = _encapsCache;
282                 _encapsCache = _encapsStack;
283                 _encapsStack = null;
284                 _encapsCache.reset();
285             }
286 
287             _startSeq = -1;
288             _sliceValues = true;
289         }
290 
291         /// <summary>
292         /// Sets the value factory manager to use when marshaling value instances. If the stream
293         /// was initialized with a communicator, the communicator's value factory manager will
294         /// be used by default.
295         /// </summary>
296         /// <param name="vfm">The value factory manager.</param>
setValueFactoryManager(ValueFactoryManager vfm)297         public void setValueFactoryManager(ValueFactoryManager vfm)
298         {
299             _valueFactoryManager = vfm;
300         }
301 
302         /// <summary>
303         /// Sets the logger to use when logging trace messages. If the stream
304         /// was initialized with a communicator, the communicator's logger will
305         /// be used by default.
306         /// </summary>
307         /// <param name="logger">The logger to use for logging trace messages.</param>
setLogger(Logger logger)308         public void setLogger(Logger logger)
309         {
310             _logger = logger;
311         }
312 
313         /// <summary>
314         /// Sets the compact ID resolver to use when unmarshaling value and exception
315         /// instances. If the stream was initialized with a communicator, the communicator's
316         /// resolver will be used by default.
317         /// </summary>
318         /// <param name="r">The compact ID resolver.</param>
setCompactIdResolver(System.Func<int, string> r)319         public void setCompactIdResolver(System.Func<int, string> r)
320         {
321             _compactIdResolver = r;
322         }
323 
324         /// <summary>
325         /// Sets the class resolver, which the stream will use when attempting to unmarshal
326         /// a value or exception. If the stream was initialized with a communicator, the communicator's
327         /// resolver will be used by default.
328         /// </summary>
329         /// <param name="r">The class resolver.</param>
setClassResolver(System.Func<string, Type> r)330         public void setClassResolver(System.Func<string, Type> r)
331         {
332             _classResolver = r;
333         }
334 
335         /// <summary>
336         /// Determines the behavior of the stream when extracting instances of Slice classes.
337         /// An instance is "sliced" when a factory cannot be found for a Slice type ID.
338         /// The stream's default behavior is to slice instances.
339         /// </summary>
340         /// <param name="b">If true (the default), slicing is enabled; if false,
341         /// slicing is disabled. If slicing is disabled and the stream encounters a Slice type ID
342         /// during decoding for which no value factory is installed, it raises NoValueFactoryException.
343         /// </param>
setSliceValues(bool b)344         public void setSliceValues(bool b)
345         {
346             _sliceValues = b;
347         }
348 
349         /// <summary>
350         /// Determines whether the stream logs messages about slicing instances of Slice values.
351         /// </summary>
352         /// <param name="b">True to enable logging, false to disable logging.</param>
setTraceSlicing(bool b)353         public void setTraceSlicing(bool b)
354         {
355             _traceSlicing = b;
356         }
357 
358         /// <summary>
359         /// Set the maximum depth allowed for graph of Slice class instances.
360         /// </summary>
361         /// <param name="classGraphDepthMax">The maximum depth.</param>
setClassGraphDepthMax(int classGraphDepthMax)362         public void setClassGraphDepthMax(int classGraphDepthMax)
363         {
364             if(classGraphDepthMax < 1)
365             {
366                 _classGraphDepthMax = 0x7fffffff;
367             }
368             else
369             {
370                 _classGraphDepthMax = classGraphDepthMax;
371             }
372         }
373 
374         /// <summary>
375         /// Retrieves the closure object associated with this stream.
376         /// </summary>
377         /// <returns>The closure object.</returns>
getClosure()378         public object getClosure()
379         {
380             return _closure;
381         }
382 
383         /// <summary>
384         /// Associates a closure object with this stream.
385         /// </summary>
386         /// <param name="p">The new closure object.</param>
387         /// <returns>The previous closure object, or null.</returns>
setClosure(object p)388         public object setClosure(object p)
389         {
390             object prev = _closure;
391             _closure = p;
392             return prev;
393         }
394 
instance()395         public IceInternal.Instance instance()
396         {
397             return _instance;
398         }
399 
400         /// <summary>
401         /// Swaps the contents of one stream with another.
402         /// </summary>
403         /// <param name="other">The other stream.</param>
swap(InputStream other)404         public void swap(InputStream other)
405         {
406             Debug.Assert(_instance == other._instance);
407 
408             IceInternal.Buffer tmpBuf = other._buf;
409             other._buf = _buf;
410             _buf = tmpBuf;
411 
412             EncodingVersion tmpEncoding = other._encoding;
413             other._encoding = _encoding;
414             _encoding = tmpEncoding;
415 
416             bool tmpTraceSlicing = other._traceSlicing;
417             other._traceSlicing = _traceSlicing;
418             _traceSlicing = tmpTraceSlicing;
419 
420             object tmpClosure = other._closure;
421             other._closure = _closure;
422             _closure = tmpClosure;
423 
424             bool tmpSliceValues = other._sliceValues;
425             other._sliceValues = _sliceValues;
426             _sliceValues = tmpSliceValues;
427 
428             int tmpClassGraphDepthMax = other._classGraphDepthMax;
429             other._classGraphDepthMax = _classGraphDepthMax;
430             _classGraphDepthMax = tmpClassGraphDepthMax;
431 
432             //
433             // Swap is never called for InputStreams that have encapsulations being read. However,
434             // encapsulations might still be set in case un-marshalling failed. We just
435             // reset the encapsulations if there are still some set.
436             //
437             resetEncapsulation();
438             other.resetEncapsulation();
439 
440             int tmpStartSeq = other._startSeq;
441             other._startSeq = _startSeq;
442             _startSeq = tmpStartSeq;
443 
444             int tmpMinSeqSize = other._minSeqSize;
445             other._minSeqSize = _minSeqSize;
446             _minSeqSize = tmpMinSeqSize;
447 
448             ValueFactoryManager tmpVfm = other._valueFactoryManager;
449             other._valueFactoryManager = _valueFactoryManager;
450             _valueFactoryManager = tmpVfm;
451 
452             Logger tmpLogger = other._logger;
453             other._logger = _logger;
454             _logger = tmpLogger;
455 
456             System.Func<int, string> tmpCompactIdResolver = other._compactIdResolver;
457             other._compactIdResolver = _compactIdResolver;
458             _compactIdResolver = tmpCompactIdResolver;
459 
460             System.Func<string, Type> tmpClassResolver = other._classResolver;
461             other._classResolver = _classResolver;
462             _classResolver = tmpClassResolver;
463         }
464 
resetEncapsulation()465         private void resetEncapsulation()
466         {
467             _encapsStack = null;
468         }
469 
470         /// <summary>
471         /// Resizes the stream to a new size.
472         /// </summary>
473         /// <param name="sz">The new size.</param>
resize(int sz)474         public void resize(int sz)
475         {
476             _buf.resize(sz, true);
477             _buf.b.position(sz);
478         }
479 
getBuffer()480         public IceInternal.Buffer getBuffer()
481         {
482             return _buf;
483         }
484 
485         /// <summary>
486         /// Marks the start of a class instance.
487         /// </summary>
startValue()488         public void startValue()
489         {
490             Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
491             _encapsStack.decoder.startInstance(SliceType.ValueSlice);
492         }
493 
494         /// <summary>
495         /// Marks the end of a class instance.
496         /// </summary>
497         /// <param name="preserve">True if unknown slices should be preserved, false otherwise.</param>
498         /// <returns>A SlicedData object containing the preserved slices for unknown types.</returns>
endValue(bool preserve)499         public SlicedData endValue(bool preserve)
500         {
501             Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
502             return _encapsStack.decoder.endInstance(preserve);
503         }
504 
505         /// <summary>
506         /// Marks the start of a user exception.
507         /// </summary>
startException()508         public void startException()
509         {
510             Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
511             _encapsStack.decoder.startInstance(SliceType.ExceptionSlice);
512         }
513 
514         /// <summary>
515         /// Marks the end of a user exception.
516         /// </summary>
517         /// <param name="preserve">True if unknown slices should be preserved, false otherwise.</param>
518         /// <returns>A SlicedData object containing the preserved slices for unknown types.</returns>
endException(bool preserve)519         public SlicedData endException(bool preserve)
520         {
521             Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
522             return _encapsStack.decoder.endInstance(preserve);
523         }
524 
525         /// <summary>
526         /// Reads the start of an encapsulation.
527         /// </summary>
528         /// <returns>The encapsulation encoding version.</returns>
startEncapsulation()529         public EncodingVersion startEncapsulation()
530         {
531             Encaps curr = _encapsCache;
532             if(curr != null)
533             {
534                 curr.reset();
535                 _encapsCache = _encapsCache.next;
536             }
537             else
538             {
539                 curr = new Encaps();
540             }
541             curr.next = _encapsStack;
542             _encapsStack = curr;
543 
544             _encapsStack.start = _buf.b.position();
545 
546             //
547             // I don't use readSize() for encapsulations, because when creating an encapsulation,
548             // I must know in advance how many bytes the size information will require in the data
549             // stream. If I use an Int, it is always 4 bytes. For readSize(), it could be 1 or 5 bytes.
550             //
551             int sz = readInt();
552             if(sz < 6)
553             {
554                 throw new UnmarshalOutOfBoundsException();
555             }
556             if(sz - 4 > _buf.b.remaining())
557             {
558                 throw new UnmarshalOutOfBoundsException();
559             }
560             _encapsStack.sz = sz;
561 
562             EncodingVersion encoding = new EncodingVersion();
563             encoding.ice_readMembers(this);
564             Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported.
565             _encapsStack.setEncoding(encoding);
566 
567             return encoding;
568         }
569 
570         /// <summary>
571         /// Ends the previous encapsulation.
572         /// </summary>
endEncapsulation()573         public void endEncapsulation()
574         {
575             Debug.Assert(_encapsStack != null);
576 
577             if(!_encapsStack.encoding_1_0)
578             {
579                 skipOptionals();
580                 if(_buf.b.position() != _encapsStack.start + _encapsStack.sz)
581                 {
582                     throw new EncapsulationException();
583                 }
584             }
585             else if(_buf.b.position() != _encapsStack.start + _encapsStack.sz)
586             {
587                 if(_buf.b.position() + 1 != _encapsStack.start + _encapsStack.sz)
588                 {
589                     throw new EncapsulationException();
590                 }
591 
592                 //
593                 // Ice version < 3.3 had a bug where user exceptions with
594                 // class members could be encoded with a trailing byte
595                 // when dispatched with AMD. So we tolerate an extra byte
596                 // in the encapsulation.
597                 //
598                 try
599                 {
600                     _buf.b.get();
601                 }
602                 catch(InvalidOperationException ex)
603                 {
604                     throw new UnmarshalOutOfBoundsException(ex);
605                 }
606             }
607 
608             Encaps curr = _encapsStack;
609             _encapsStack = curr.next;
610             curr.next = _encapsCache;
611             _encapsCache = curr;
612             _encapsCache.reset();
613         }
614 
615         /// <summary>
616         /// Skips an empty encapsulation.
617         /// </summary>
618         /// <returns>The encapsulation's encoding version.</returns>
skipEmptyEncapsulation()619         public EncodingVersion skipEmptyEncapsulation()
620         {
621             int sz = readInt();
622             if(sz < 6)
623             {
624                 throw new EncapsulationException();
625             }
626             if(sz - 4 > _buf.b.remaining())
627             {
628                 throw new UnmarshalOutOfBoundsException();
629             }
630 
631             var encoding = new EncodingVersion();
632             encoding.ice_readMembers(this);
633             Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported.
634 
635             if(encoding.Equals(Util.Encoding_1_0))
636             {
637                 if(sz != 6)
638                 {
639                     throw new EncapsulationException();
640                 }
641             }
642             else
643             {
644                 // Skip the optional content of the encapsulation if we are expecting an
645                 // empty encapsulation.
646                 _buf.b.position(_buf.b.position() + sz - 6);
647             }
648             return encoding;
649         }
650 
651         /// <summary>
652         /// Returns a blob of bytes representing an encapsulation. The encapsulation's encoding version
653         /// is returned in the argument.
654         /// </summary>
655         /// <param name="encoding">The encapsulation's encoding version.</param>
656         /// <returns>The encoded encapsulation.</returns>
readEncapsulation(out EncodingVersion encoding)657         public byte[] readEncapsulation(out EncodingVersion encoding)
658         {
659             int sz = readInt();
660             if(sz < 6)
661             {
662                 throw new UnmarshalOutOfBoundsException();
663             }
664 
665             if(sz - 4 > _buf.b.remaining())
666             {
667                 throw new UnmarshalOutOfBoundsException();
668             }
669 
670             encoding = new EncodingVersion();
671             encoding.ice_readMembers(this);
672             _buf.b.position(_buf.b.position() - 6);
673 
674             byte[] v = new byte[sz];
675             try
676             {
677                 _buf.b.get(v);
678                 return v;
679             }
680             catch(InvalidOperationException ex)
681             {
682                 throw new UnmarshalOutOfBoundsException(ex);
683             }
684         }
685 
686         /// <summary>
687         /// Determines the current encoding version.
688         /// </summary>
689         /// <returns>The encoding version.</returns>
getEncoding()690         public EncodingVersion getEncoding()
691         {
692             return _encapsStack != null ? _encapsStack.encoding : _encoding;
693         }
694 
695         /// <summary>
696         /// Determines the size of the current encapsulation, excluding the encapsulation header.
697         /// </summary>
698         /// <returns>The size of the encapsulated data.</returns>
getEncapsulationSize()699         public int getEncapsulationSize()
700         {
701             Debug.Assert(_encapsStack != null);
702             return _encapsStack.sz - 6;
703         }
704 
705         /// <summary>
706         /// Skips over an encapsulation.
707         /// </summary>
708         /// <returns>The encoding version of the skipped encapsulation.</returns>
skipEncapsulation()709         public EncodingVersion skipEncapsulation()
710         {
711             int sz = readInt();
712             if(sz < 6)
713             {
714                 throw new UnmarshalOutOfBoundsException();
715             }
716             EncodingVersion encoding = new EncodingVersion();
717             encoding.ice_readMembers(this);
718             try
719             {
720                 _buf.b.position(_buf.b.position() + sz - 6);
721             }
722             catch(ArgumentOutOfRangeException ex)
723             {
724                 throw new UnmarshalOutOfBoundsException(ex);
725             }
726             return encoding;
727         }
728 
729         /// <summary>
730         /// Reads the start of a class instance or exception slice.
731         /// </summary>
732         /// <returns>The Slice type ID for this slice.</returns>
startSlice()733         public string startSlice() // Returns type ID of next slice
734         {
735             Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
736             return _encapsStack.decoder.startSlice();
737         }
738 
739         /// <summary>
740         /// Indicates that the end of a class instance or exception slice has been reached.
741         /// </summary>
endSlice()742         public void endSlice()
743         {
744             Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
745             _encapsStack.decoder.endSlice();
746         }
747 
748         /// <summary>
749         /// Skips over a class instance or exception slice.
750         /// </summary>
skipSlice()751         public void skipSlice()
752         {
753             Debug.Assert(_encapsStack != null && _encapsStack.decoder != null);
754             _encapsStack.decoder.skipSlice();
755         }
756 
757         /// <summary>
758         /// Indicates that unmarshaling is complete, except for any class instances. The application must call this
759         /// method only if the stream actually contains class instances. Calling readPendingValues triggers the
760         /// calls to the System.Action delegates to inform the application that unmarshaling of an instance
761         /// is complete.
762         /// </summary>
readPendingValues()763         public void readPendingValues()
764         {
765             if(_encapsStack != null && _encapsStack.decoder != null)
766             {
767                 _encapsStack.decoder.readPendingValues();
768             }
769             else if(_encapsStack != null ? _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0))
770             {
771                 //
772                 // If using the 1.0 encoding and no instances were read, we
773                 // still read an empty sequence of pending instances if
774                 // requested (i.e.: if this is called).
775                 //
776                 // This is required by the 1.0 encoding, even if no instances
777                 // are written we do marshal an empty sequence if marshaled
778                 // data types use classes.
779                 //
780                 skipSize();
781             }
782         }
783 
784         /// <summary>
785         /// Extracts a size from the stream.
786         /// </summary>
787         /// <returns>The extracted size.</returns>
readSize()788         public int readSize()
789         {
790             try
791             {
792                 byte b = _buf.b.get();
793                 if(b == 255)
794                 {
795                     int v = _buf.b.getInt();
796                     if(v < 0)
797                     {
798                         throw new UnmarshalOutOfBoundsException();
799                     }
800                     return v;
801                 }
802                 else
803                 {
804                     return b; // byte is unsigned
805                 }
806             }
807             catch(InvalidOperationException ex)
808             {
809                 throw new UnmarshalOutOfBoundsException(ex);
810             }
811         }
812 
813         /// <summary>
814         /// Reads and validates a sequence size.
815         /// </summary>
816         /// <returns>The extracted size.</returns>
readAndCheckSeqSize(int minSize)817         public int readAndCheckSeqSize(int minSize)
818         {
819             int sz = readSize();
820 
821             if(sz == 0)
822             {
823                 return 0;
824             }
825 
826             //
827             // The _startSeq variable points to the start of the sequence for which
828             // we expect to read at least _minSeqSize bytes from the stream.
829             //
830             // If not initialized or if we already read more data than _minSeqSize,
831             // we reset _startSeq and _minSeqSize for this sequence (possibly a
832             // top-level sequence or enclosed sequence it doesn't really matter).
833             //
834             // Otherwise, we are reading an enclosed sequence and we have to bump
835             // _minSeqSize by the minimum size that this sequence will  require on
836             // the stream.
837             //
838             // The goal of this check is to ensure that when we start un-marshalling
839             // a new sequence, we check the minimal size of this new sequence against
840             // the estimated remaining buffer size. This estimatation is based on
841             // the minimum size of the enclosing sequences, it's _minSeqSize.
842             //
843             if(_startSeq == -1 || _buf.b.position() > (_startSeq + _minSeqSize))
844             {
845                 _startSeq = _buf.b.position();
846                 _minSeqSize = sz * minSize;
847             }
848             else
849             {
850                 _minSeqSize += sz * minSize;
851             }
852 
853             //
854             // If there isn't enough data to read on the stream for the sequence (and
855             // possibly enclosed sequences), something is wrong with the marshalled
856             // data: it's claiming having more data that what is possible to read.
857             //
858             if(_startSeq + _minSeqSize > _buf.size())
859             {
860                 throw new UnmarshalOutOfBoundsException();
861             }
862 
863             return sz;
864         }
865 
866         /// <summary>
867         /// Reads a blob of bytes from the stream. The length of the given array determines how many bytes are read.
868         /// </summary>
869         /// <param name="v">Bytes from the stream.</param>
readBlob(byte[] v)870         public void readBlob(byte[] v)
871         {
872             try
873             {
874                 _buf.b.get(v);
875             }
876             catch(InvalidOperationException ex)
877             {
878                 throw new UnmarshalOutOfBoundsException(ex);
879             }
880         }
881 
882         /// <summary>
883         /// Reads a blob of bytes from the stream.
884         /// </summary>
885         /// <param name="sz">The number of bytes to read.</param>
886         /// <returns>The requested bytes as a byte array.</returns>
readBlob(int sz)887         public byte[] readBlob(int sz)
888         {
889             if(_buf.b.remaining() < sz)
890             {
891                 throw new UnmarshalOutOfBoundsException();
892             }
893             byte[] v = new byte[sz];
894             try
895             {
896                 _buf.b.get(v);
897                 return v;
898             }
899             catch(InvalidOperationException ex)
900             {
901                 throw new UnmarshalOutOfBoundsException(ex);
902             }
903         }
904 
905         /// <summary>
906         /// Determine if an optional value is available for reading.
907         /// </summary>
908         /// <param name="tag">The tag associated with the value.</param>
909         /// <param name="expectedFormat">The optional format for the value.</param>
910         /// <returns>True if the value is present, false otherwise.</returns>
readOptional(int tag, OptionalFormat expectedFormat)911         public bool readOptional(int tag, OptionalFormat expectedFormat)
912         {
913             Debug.Assert(_encapsStack != null);
914             if(_encapsStack.decoder != null)
915             {
916                 return _encapsStack.decoder.readOptional(tag, expectedFormat);
917             }
918             else
919             {
920                 return readOptImpl(tag, expectedFormat);
921             }
922         }
923 
924         /// <summary>
925         /// Extracts a byte value from the stream.
926         /// </summary>
927         /// <returns>The extracted byte.</returns>
readByte()928         public byte readByte()
929         {
930             try
931             {
932                 return _buf.b.get();
933             }
934             catch(InvalidOperationException ex)
935             {
936                 throw new UnmarshalOutOfBoundsException(ex);
937             }
938         }
939 
940         /// <summary>
941         /// Extracts an optional byte value from the stream.
942         /// </summary>
943         /// <param name="tag">The numeric tag associated with the value.</param>
944         /// <returns>The optional value.</returns>
readByte(int tag)945         public Optional<byte> readByte(int tag)
946         {
947             if(readOptional(tag, OptionalFormat.F1))
948             {
949                 return new Optional<byte>(readByte());
950             }
951             else
952             {
953                 return new Optional<byte>();
954             }
955         }
956 
957         /// <summary>
958         /// Extracts an optional byte value from the stream.
959         /// </summary>
960         /// <param name="tag">The numeric tag associated with the value.</param>
961         /// <param name="isset">True if the optional value is present, false otherwise.</param>
962         /// <param name="v">The optional value.</param>
readByte(int tag, out bool isset, out byte v)963         public void readByte(int tag, out bool isset, out byte v)
964         {
965             if(isset = readOptional(tag, OptionalFormat.F1))
966             {
967                 v = readByte();
968             }
969             else
970             {
971                 v = 0;
972             }
973         }
974 
975         /// <summary>
976         /// Extracts a sequence of byte values from the stream.
977         /// </summary>
978         /// <returns>The extracted byte sequence.</returns>
readByteSeq()979         public byte[] readByteSeq()
980         {
981             try
982             {
983                 int sz = readAndCheckSeqSize(1);
984                 byte[] v = new byte[sz];
985                 _buf.b.get(v);
986                 return v;
987             }
988             catch(InvalidOperationException ex)
989             {
990                 throw new UnmarshalOutOfBoundsException(ex);
991             }
992         }
993 
994         /// <summary>
995         /// Extracts a sequence of byte values from the stream.
996         /// </summary>
997         /// <param name="l">The extracted byte sequence as a list.</param>
readByteSeq(out List<byte> l)998         public void readByteSeq(out List<byte> l)
999         {
1000             //
1001             // Reading into an array and copy-constructing the
1002             // list is faster than constructing the list
1003             // and adding to it one element at a time.
1004             //
1005             l = new List<byte>(readByteSeq());
1006         }
1007 
1008         /// <summary>
1009         /// Extracts a sequence of byte values from the stream.
1010         /// </summary>
1011         /// <param name="l">The extracted byte sequence as a linked list.</param>
readByteSeq(out LinkedList<byte> l)1012         public void readByteSeq(out LinkedList<byte> l)
1013         {
1014             //
1015             // Reading into an array and copy-constructing the
1016             // list is faster than constructing the list
1017             // and adding to it one element at a time.
1018             //
1019             l = new LinkedList<byte>(readByteSeq());
1020         }
1021 
1022         /// <summary>
1023         /// Extracts a sequence of byte values from the stream.
1024         /// </summary>
1025         /// <param name="l">The extracted byte sequence as a queue.</param>
readByteSeq(out Queue<byte> l)1026         public void readByteSeq(out Queue<byte> l)
1027         {
1028             //
1029             // Reading into an array and copy-constructing the
1030             // queue is faster than constructing the queue
1031             // and adding to it one element at a time.
1032             //
1033             l = new Queue<byte>(readByteSeq());
1034         }
1035 
1036         /// <summary>
1037         /// Extracts a sequence of byte values from the stream.
1038         /// </summary>
1039         /// <param name="l">The extracted byte sequence as a stack.</param>
readByteSeq(out Stack<byte> l)1040         public void readByteSeq(out Stack<byte> l)
1041         {
1042             //
1043             // Reverse the contents by copying into an array first
1044             // because the stack is marshaled in top-to-bottom order.
1045             //
1046             byte[] array = readByteSeq();
1047             Array.Reverse(array);
1048             l = new Stack<byte>(array);
1049         }
1050 
1051         /// <summary>
1052         /// Extracts an optional byte sequence from the stream.
1053         /// </summary>
1054         /// <param name="tag">The numeric tag associated with the value.</param>
1055         /// <returns>The optional value.</returns>
readByteSeq(int tag)1056         public Optional<byte[]> readByteSeq(int tag)
1057         {
1058             if(readOptional(tag, OptionalFormat.VSize))
1059             {
1060                 return new Optional<byte[]>(readByteSeq());
1061             }
1062             else
1063             {
1064                 return new Optional<byte[]>();
1065             }
1066         }
1067 
1068         /// <summary>
1069         /// Extracts an optional byte sequence from the stream.
1070         /// </summary>
1071         /// <param name="tag">The numeric tag associated with the value.</param>
1072         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1073         /// <param name="v">The optional value.</param>
readByteSeq(int tag, out bool isset, out byte[] v)1074         public void readByteSeq(int tag, out bool isset, out byte[] v)
1075         {
1076             if(isset = readOptional(tag, OptionalFormat.VSize))
1077             {
1078                 v = readByteSeq();
1079             }
1080             else
1081             {
1082                 v = null;
1083             }
1084         }
1085 
1086         /// <summary>
1087         /// Extracts a serializable object from the stream.
1088         /// </summary>
1089         /// <returns>The serializable object.</returns>
readSerializable()1090         public object readSerializable()
1091         {
1092             int sz = readAndCheckSeqSize(1);
1093             if(sz == 0)
1094             {
1095                 return null;
1096             }
1097             try
1098             {
1099                 var f = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.All, _instance));
1100                 return f.Deserialize(new IceInternal.InputStreamWrapper(sz, this));
1101             }
1102             catch(System.Exception ex)
1103             {
1104                 throw new MarshalException("cannot deserialize object:", ex);
1105             }
1106         }
1107 
1108         /// <summary>
1109         /// Extracts a boolean value from the stream.
1110         /// </summary>
1111         /// <returns>The extracted boolean.</returns>
readBool()1112         public bool readBool()
1113         {
1114             try
1115             {
1116                 return _buf.b.get() == 1;
1117             }
1118             catch(InvalidOperationException ex)
1119             {
1120                 throw new UnmarshalOutOfBoundsException(ex);
1121             }
1122         }
1123 
1124         /// <summary>
1125         /// Extracts an optional boolean value from the stream.
1126         /// </summary>
1127         /// <param name="tag">The numeric tag associated with the value.</param>
1128         /// <returns>The optional value.</returns>
readBool(int tag)1129         public Optional<bool> readBool(int tag)
1130         {
1131             if(readOptional(tag, OptionalFormat.F1))
1132             {
1133                 return new Optional<bool>(readBool());
1134             }
1135             else
1136             {
1137                 return new Optional<bool>();
1138             }
1139         }
1140 
1141         /// <summary>
1142         /// Extracts an optional boolean value from the stream.
1143         /// </summary>
1144         /// <param name="tag">The numeric tag associated with the value.</param>
1145         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1146         /// <param name="v">The optional value.</param>
readBool(int tag, out bool isset, out bool v)1147         public void readBool(int tag, out bool isset, out bool v)
1148         {
1149             if(isset = readOptional(tag, OptionalFormat.F1))
1150             {
1151                 v = readBool();
1152             }
1153             else
1154             {
1155                 v = false;
1156             }
1157         }
1158 
1159         /// <summary>
1160         /// Extracts a sequence of boolean values from the stream.
1161         /// </summary>
1162         /// <returns>The extracted boolean sequence.</returns>
readBoolSeq()1163         public bool[] readBoolSeq()
1164         {
1165             try
1166             {
1167                 int sz = readAndCheckSeqSize(1);
1168                 bool[] v = new bool[sz];
1169                 _buf.b.getBoolSeq(v);
1170                 return v;
1171             }
1172             catch(InvalidOperationException ex)
1173             {
1174                 throw new UnmarshalOutOfBoundsException(ex);
1175             }
1176         }
1177 
1178         /// <summary>
1179         /// Extracts a sequence of boolean values from the stream.
1180         /// </summary>
1181         /// <param name="l">The extracted boolean sequence as a list.</param>
readBoolSeq(out List<bool> l)1182         public void readBoolSeq(out List<bool> l)
1183         {
1184             //
1185             // Reading into an array and copy-constructing the
1186             // list is faster than constructing the list
1187             // and adding to it one element at a time.
1188             //
1189             l = new List<bool>(readBoolSeq());
1190         }
1191 
1192         /// <summary>
1193         /// Extracts a sequence of boolean values from the stream.
1194         /// </summary>
1195         /// <param name="l">The extracted boolean sequence as a linked list.</param>
readBoolSeq(out LinkedList<bool> l)1196         public void readBoolSeq(out LinkedList<bool> l)
1197         {
1198             //
1199             // Reading into an array and copy-constructing the
1200             // list is faster than constructing the list
1201             // and adding to it one element at a time.
1202             //
1203             l = new LinkedList<bool>(readBoolSeq());
1204         }
1205 
1206         /// <summary>
1207         /// Extracts a sequence of boolean values from the stream.
1208         /// </summary>
1209         /// <param name="l">The extracted boolean sequence as a queue.</param>
readBoolSeq(out Queue<bool> l)1210         public void readBoolSeq(out Queue<bool> l)
1211         {
1212             //
1213             // Reading into an array and copy-constructing the
1214             // queue is faster than constructing the queue
1215             // and adding to it one element at a time.
1216             //
1217             l = new Queue<bool>(readBoolSeq());
1218         }
1219 
1220         /// <summary>
1221         /// Extracts a sequence of boolean values from the stream.
1222         /// </summary>
1223         /// <param name="l">The extracted boolean sequence as a stack.</param>
readBoolSeq(out Stack<bool> l)1224         public void readBoolSeq(out Stack<bool> l)
1225         {
1226             //
1227             // Reverse the contents by copying into an array first
1228             // because the stack is marshaled in top-to-bottom order.
1229             //
1230             bool[] array = readBoolSeq();
1231             Array.Reverse(array);
1232             l = new Stack<bool>(array);
1233         }
1234 
1235         /// <summary>
1236         /// Extracts an optional boolean sequence from the stream.
1237         /// </summary>
1238         /// <param name="tag">The numeric tag associated with the value.</param>
1239         /// <returns>The optional value.</returns>
readBoolSeq(int tag)1240         public Optional<bool[]> readBoolSeq(int tag)
1241         {
1242             if(readOptional(tag, OptionalFormat.VSize))
1243             {
1244                 return new Optional<bool[]>(readBoolSeq());
1245             }
1246             else
1247             {
1248                 return new Optional<bool[]>();
1249             }
1250         }
1251 
1252         /// <summary>
1253         /// Extracts an optional boolean sequence from the stream.
1254         /// </summary>
1255         /// <param name="tag">The numeric tag associated with the value.</param>
1256         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1257         /// <param name="v">The optional value.</param>
readBoolSeq(int tag, out bool isset, out bool[] v)1258         public void readBoolSeq(int tag, out bool isset, out bool[] v)
1259         {
1260             if(isset = readOptional(tag, OptionalFormat.VSize))
1261             {
1262                 v = readBoolSeq();
1263             }
1264             else
1265             {
1266                 v = null;
1267             }
1268         }
1269 
1270         /// <summary>
1271         /// Extracts a short value from the stream.
1272         /// </summary>
1273         /// <returns>The extracted short.</returns>
readShort()1274         public short readShort()
1275         {
1276             try
1277             {
1278                 return _buf.b.getShort();
1279             }
1280             catch(InvalidOperationException ex)
1281             {
1282                 throw new UnmarshalOutOfBoundsException(ex);
1283             }
1284         }
1285 
1286         /// <summary>
1287         /// Extracts an optional short value from the stream.
1288         /// </summary>
1289         /// <param name="tag">The numeric tag associated with the value.</param>
1290         /// <returns>The optional value.</returns>
readShort(int tag)1291         public Optional<short> readShort(int tag)
1292         {
1293             if(readOptional(tag, OptionalFormat.F2))
1294             {
1295                 return new Optional<short>(readShort());
1296             }
1297             else
1298             {
1299                 return new Optional<short>();
1300             }
1301         }
1302 
1303         /// <summary>
1304         /// Extracts an optional short value from the stream.
1305         /// </summary>
1306         /// <param name="tag">The numeric tag associated with the value.</param>
1307         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1308         /// <param name="v">The optional value.</param>
readShort(int tag, out bool isset, out short v)1309         public void readShort(int tag, out bool isset, out short v)
1310         {
1311             if(isset = readOptional(tag, OptionalFormat.F2))
1312             {
1313                 v = readShort();
1314             }
1315             else
1316             {
1317                 v = 0;
1318             }
1319         }
1320 
1321         /// <summary>
1322         /// Extracts a sequence of short values from the stream.
1323         /// </summary>
1324         /// <returns>The extracted short sequence.</returns>
readShortSeq()1325         public short[] readShortSeq()
1326         {
1327             try
1328             {
1329                 int sz = readAndCheckSeqSize(2);
1330                 short[] v = new short[sz];
1331                 _buf.b.getShortSeq(v);
1332                 return v;
1333             }
1334             catch(InvalidOperationException ex)
1335             {
1336                 throw new UnmarshalOutOfBoundsException(ex);
1337             }
1338         }
1339 
1340         /// <summary>
1341         /// Extracts a sequence of short values from the stream.
1342         /// </summary>
1343         /// <param name="l">The extracted short sequence as a list.</param>
readShortSeq(out List<short> l)1344         public void readShortSeq(out List<short> l)
1345         {
1346             //
1347             // Reading into an array and copy-constructing the
1348             // list is faster than constructing the list
1349             // and adding to it one element at a time.
1350             //
1351             l = new List<short>(readShortSeq());
1352         }
1353 
1354         /// <summary>
1355         /// Extracts a sequence of short values from the stream.
1356         /// </summary>
1357         /// <param name="l">The extracted short sequence as a linked list.</param>
readShortSeq(out LinkedList<short> l)1358         public void readShortSeq(out LinkedList<short> l)
1359         {
1360             //
1361             // Reading into an array and copy-constructing the
1362             // list is faster than constructing the list
1363             // and adding to it one element at a time.
1364             //
1365             l = new LinkedList<short>(readShortSeq());
1366         }
1367 
1368         /// <summary>
1369         /// Extracts a sequence of short values from the stream.
1370         /// </summary>
1371         /// <param name="l">The extracted short sequence as a queue.</param>
readShortSeq(out Queue<short> l)1372         public void readShortSeq(out Queue<short> l)
1373         {
1374             //
1375             // Reading into an array and copy-constructing the
1376             // queue is faster than constructing the queue
1377             // and adding to it one element at a time.
1378             //
1379             l = new Queue<short>(readShortSeq());
1380         }
1381 
1382         /// <summary>
1383         /// Extracts a sequence of short values from the stream.
1384         /// </summary>
1385         /// <param name="l">The extracted short sequence as a stack.</param>
readShortSeq(out Stack<short> l)1386         public void readShortSeq(out Stack<short> l)
1387         {
1388             //
1389             // Reverse the contents by copying into an array first
1390             // because the stack is marshaled in top-to-bottom order.
1391             //
1392             short[] array = readShortSeq();
1393             Array.Reverse(array);
1394             l = new Stack<short>(array);
1395         }
1396 
1397         /// <summary>
1398         /// Extracts an optional short sequence from the stream.
1399         /// </summary>
1400         /// <param name="tag">The numeric tag associated with the value.</param>
1401         /// <returns>The optional value.</returns>
readShortSeq(int tag)1402         public Optional<short[]> readShortSeq(int tag)
1403         {
1404             if(readOptional(tag, OptionalFormat.VSize))
1405             {
1406                 skipSize();
1407                 return new Optional<short[]>(readShortSeq());
1408             }
1409             else
1410             {
1411                 return new Optional<short[]>();
1412             }
1413         }
1414 
1415         /// <summary>
1416         /// Extracts an optional short sequence from the stream.
1417         /// </summary>
1418         /// <param name="tag">The numeric tag associated with the value.</param>
1419         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1420         /// <param name="v">The optional value.</param>
readShortSeq(int tag, out bool isset, out short[] v)1421         public void readShortSeq(int tag, out bool isset, out short[] v)
1422         {
1423             if(isset = readOptional(tag, OptionalFormat.VSize))
1424             {
1425                 skipSize();
1426                 v = readShortSeq();
1427             }
1428             else
1429             {
1430                 v = null;
1431             }
1432         }
1433 
1434         /// <summary>
1435         /// Extracts an int value from the stream.
1436         /// </summary>
1437         /// <returns>The extracted int.</returns>
readInt()1438         public int readInt()
1439         {
1440             try
1441             {
1442                 return _buf.b.getInt();
1443             }
1444             catch(InvalidOperationException ex)
1445             {
1446                 throw new UnmarshalOutOfBoundsException(ex);
1447             }
1448         }
1449 
1450         /// <summary>
1451         /// Extracts an optional int value from the stream.
1452         /// </summary>
1453         /// <param name="tag">The numeric tag associated with the value.</param>
1454         /// <returns>The optional value.</returns>
readInt(int tag)1455         public Optional<int> readInt(int tag)
1456         {
1457             if(readOptional(tag, OptionalFormat.F4))
1458             {
1459                 return new Optional<int>(readInt());
1460             }
1461             else
1462             {
1463                 return new Optional<int>();
1464             }
1465         }
1466 
1467         /// <summary>
1468         /// Extracts an optional int value from the stream.
1469         /// </summary>
1470         /// <param name="tag">The numeric tag associated with the value.</param>
1471         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1472         /// <param name="v">The optional value.</param>
readInt(int tag, out bool isset, out int v)1473         public void readInt(int tag, out bool isset, out int v)
1474         {
1475             if(isset = readOptional(tag, OptionalFormat.F4))
1476             {
1477                 v = readInt();
1478             }
1479             else
1480             {
1481                 v = 0;
1482             }
1483         }
1484 
1485         /// <summary>
1486         /// Extracts a sequence of int values from the stream.
1487         /// </summary>
1488         /// <returns>The extracted int sequence.</returns>
readIntSeq()1489         public int[] readIntSeq()
1490         {
1491             try
1492             {
1493                 int sz = readAndCheckSeqSize(4);
1494                 int[] v = new int[sz];
1495                 _buf.b.getIntSeq(v);
1496                 return v;
1497             }
1498             catch(InvalidOperationException ex)
1499             {
1500                 throw new UnmarshalOutOfBoundsException(ex);
1501             }
1502         }
1503 
1504         /// <summary>
1505         /// Extracts a sequence of int values from the stream.
1506         /// </summary>
1507         /// <param name="l">The extracted int sequence as a list.</param>
readIntSeq(out List<int> l)1508         public void readIntSeq(out List<int> l)
1509         {
1510             //
1511             // Reading into an array and copy-constructing the
1512             // list is faster than constructing the list
1513             // and adding to it one element at a time.
1514             //
1515             l = new List<int>(readIntSeq());
1516         }
1517 
1518         /// <summary>
1519         /// Extracts a sequence of int values from the stream.
1520         /// </summary>
1521         /// <param name="l">The extracted int sequence as a linked list.</param>
readIntSeq(out LinkedList<int> l)1522         public void readIntSeq(out LinkedList<int> l)
1523         {
1524             try
1525             {
1526                 int sz = readAndCheckSeqSize(4);
1527                 l = new LinkedList<int>();
1528                 for(int i = 0; i < sz; ++i)
1529                 {
1530                     l.AddLast(_buf.b.getInt());
1531                 }
1532             }
1533             catch(InvalidOperationException ex)
1534             {
1535                 throw new UnmarshalOutOfBoundsException(ex);
1536             }
1537         }
1538 
1539         /// <summary>
1540         /// Extracts a sequence of int values from the stream.
1541         /// </summary>
1542         /// <param name="l">The extracted int sequence as a queue.</param>
readIntSeq(out Queue<int> l)1543         public void readIntSeq(out Queue<int> l)
1544         {
1545             //
1546             // Reading into an array and copy-constructing the
1547             // queue takes the same time as constructing the queue
1548             // and adding to it one element at a time, so
1549             // we avoid the copy.
1550             //
1551             try
1552             {
1553                 int sz = readAndCheckSeqSize(4);
1554                 l = new Queue<int>(sz);
1555                 for(int i = 0; i < sz; ++i)
1556                 {
1557                     l.Enqueue(_buf.b.getInt());
1558                 }
1559             }
1560             catch(InvalidOperationException ex)
1561             {
1562                 throw new UnmarshalOutOfBoundsException(ex);
1563             }
1564         }
1565 
1566         /// <summary>
1567         /// Extracts a sequence of int values from the stream.
1568         /// </summary>
1569         /// <param name="l">The extracted int sequence as a stack.</param>
readIntSeq(out Stack<int> l)1570         public void readIntSeq(out Stack<int> l)
1571         {
1572             //
1573             // Reverse the contents by copying into an array first
1574             // because the stack is marshaled in top-to-bottom order.
1575             //
1576             int[] array = readIntSeq();
1577             Array.Reverse(array);
1578             l = new Stack<int>(array);
1579         }
1580 
1581         /// <summary>
1582         /// Extracts an optional int sequence from the stream.
1583         /// </summary>
1584         /// <param name="tag">The numeric tag associated with the value.</param>
1585         /// <returns>The optional value.</returns>
readIntSeq(int tag)1586         public Optional<int[]> readIntSeq(int tag)
1587         {
1588             if(readOptional(tag, OptionalFormat.VSize))
1589             {
1590                 skipSize();
1591                 return new Optional<int[]>(readIntSeq());
1592             }
1593             else
1594             {
1595                 return new Optional<int[]>();
1596             }
1597         }
1598 
1599         /// <summary>
1600         /// Extracts an optional int sequence from the stream.
1601         /// </summary>
1602         /// <param name="tag">The numeric tag associated with the value.</param>
1603         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1604         /// <param name="v">The optional value.</param>
readIntSeq(int tag, out bool isset, out int[] v)1605         public void readIntSeq(int tag, out bool isset, out int[] v)
1606         {
1607             if(isset = readOptional(tag, OptionalFormat.VSize))
1608             {
1609                 skipSize();
1610                 v = readIntSeq();
1611             }
1612             else
1613             {
1614                 v = null;
1615             }
1616         }
1617 
1618         /// <summary>
1619         /// Extracts a long value from the stream.
1620         /// </summary>
1621         /// <returns>The extracted long.</returns>
readLong()1622         public long readLong()
1623         {
1624             try
1625             {
1626                 return _buf.b.getLong();
1627             }
1628             catch(InvalidOperationException ex)
1629             {
1630                 throw new UnmarshalOutOfBoundsException(ex);
1631             }
1632         }
1633 
1634         /// <summary>
1635         /// Extracts an optional long value from the stream.
1636         /// </summary>
1637         /// <param name="tag">The numeric tag associated with the value.</param>
1638         /// <returns>The optional value.</returns>
readLong(int tag)1639         public Optional<long> readLong(int tag)
1640         {
1641             if(readOptional(tag, OptionalFormat.F8))
1642             {
1643                 return new Optional<long>(readLong());
1644             }
1645             else
1646             {
1647                 return new Optional<long>();
1648             }
1649         }
1650 
1651         /// <summary>
1652         /// Extracts an optional long value from the stream.
1653         /// </summary>
1654         /// <param name="tag">The numeric tag associated with the value.</param>
1655         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1656         /// <param name="v">The optional value.</param>
readLong(int tag, out bool isset, out long v)1657         public void readLong(int tag, out bool isset, out long v)
1658         {
1659             if(isset = readOptional(tag, OptionalFormat.F8))
1660             {
1661                 v = readLong();
1662             }
1663             else
1664             {
1665                 v = 0;
1666             }
1667         }
1668 
1669         /// <summary>
1670         /// Extracts a sequence of long values from the stream.
1671         /// </summary>
1672         /// <returns>The extracted long sequence.</returns>
readLongSeq()1673         public long[] readLongSeq()
1674         {
1675             try
1676             {
1677                 int sz = readAndCheckSeqSize(8);
1678                 long[] v = new long[sz];
1679                 _buf.b.getLongSeq(v);
1680                 return v;
1681             }
1682             catch(InvalidOperationException ex)
1683             {
1684                 throw new UnmarshalOutOfBoundsException(ex);
1685             }
1686         }
1687 
1688         /// <summary>
1689         /// Extracts a sequence of long values from the stream.
1690         /// </summary>
1691         /// <param name="l">The extracted long sequence as a list.</param>
readLongSeq(out List<long> l)1692         public void readLongSeq(out List<long> l)
1693         {
1694             //
1695             // Reading into an array and copy-constructing the
1696             // list is faster than constructing the list
1697             // and adding to it one element at a time.
1698             //
1699             l = new List<long>(readLongSeq());
1700         }
1701 
1702         /// <summary>
1703         /// Extracts a sequence of long values from the stream.
1704         /// </summary>
1705         /// <param name="l">The extracted long sequence as a linked list.</param>
readLongSeq(out LinkedList<long> l)1706         public void readLongSeq(out LinkedList<long> l)
1707         {
1708             try
1709             {
1710                 int sz = readAndCheckSeqSize(4);
1711                 l = new LinkedList<long>();
1712                 for(int i = 0; i < sz; ++i)
1713                 {
1714                     l.AddLast(_buf.b.getLong());
1715                 }
1716             }
1717             catch(InvalidOperationException ex)
1718             {
1719                 throw new UnmarshalOutOfBoundsException(ex);
1720             }
1721         }
1722 
1723         /// <summary>
1724         /// Extracts a sequence of long values from the stream.
1725         /// </summary>
1726         /// <param name="l">The extracted long sequence as a queue.</param>
readLongSeq(out Queue<long> l)1727         public void readLongSeq(out Queue<long> l)
1728         {
1729             //
1730             // Reading into an array and copy-constructing the
1731             // queue takes the same time as constructing the queue
1732             // and adding to it one element at a time, so
1733             // we avoid the copy.
1734             //
1735             try
1736             {
1737                 int sz = readAndCheckSeqSize(4);
1738                 l = new Queue<long>(sz);
1739                 for(int i = 0; i < sz; ++i)
1740                 {
1741                     l.Enqueue(_buf.b.getLong());
1742                 }
1743             }
1744             catch(InvalidOperationException ex)
1745             {
1746                 throw new UnmarshalOutOfBoundsException(ex);
1747             }
1748         }
1749 
1750         /// <summary>
1751         /// Extracts a sequence of long values from the stream.
1752         /// </summary>
1753         /// <param name="l">The extracted long sequence as a stack.</param>
readLongSeq(out Stack<long> l)1754         public void readLongSeq(out Stack<long> l)
1755         {
1756             //
1757             // Reverse the contents by copying into an array first
1758             // because the stack is marshaled in top-to-bottom order.
1759             //
1760             long[] array = readLongSeq();
1761             Array.Reverse(array);
1762             l = new Stack<long>(array);
1763         }
1764 
1765         /// <summary>
1766         /// Extracts an optional long sequence from the stream.
1767         /// </summary>
1768         /// <param name="tag">The numeric tag associated with the value.</param>
1769         /// <returns>The optional value.</returns>
readLongSeq(int tag)1770         public Optional<long[]> readLongSeq(int tag)
1771         {
1772             if(readOptional(tag, OptionalFormat.VSize))
1773             {
1774                 skipSize();
1775                 return new Optional<long[]>(readLongSeq());
1776             }
1777             else
1778             {
1779                 return new Optional<long[]>();
1780             }
1781         }
1782 
1783         /// <summary>
1784         /// Extracts an optional long sequence from the stream.
1785         /// </summary>
1786         /// <param name="tag">The numeric tag associated with the value.</param>
1787         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1788         /// <param name="v">The optional value.</param>
readLongSeq(int tag, out bool isset, out long[] v)1789         public void readLongSeq(int tag, out bool isset, out long[] v)
1790         {
1791             if(isset = readOptional(tag, OptionalFormat.VSize))
1792             {
1793                 skipSize();
1794                 v = readLongSeq();
1795             }
1796             else
1797             {
1798                 v = null;
1799             }
1800         }
1801 
1802         /// <summary>
1803         /// Extracts a float value from the stream.
1804         /// </summary>
1805         /// <returns>The extracted float.</returns>
readFloat()1806         public float readFloat()
1807         {
1808             try
1809             {
1810                 return _buf.b.getFloat();
1811             }
1812             catch(InvalidOperationException ex)
1813             {
1814                 throw new UnmarshalOutOfBoundsException(ex);
1815             }
1816         }
1817 
1818         /// <summary>
1819         /// Extracts an optional float value from the stream.
1820         /// </summary>
1821         /// <param name="tag">The numeric tag associated with the value.</param>
1822         /// <returns>The optional value.</returns>
readFloat(int tag)1823         public Optional<float> readFloat(int tag)
1824         {
1825             if(readOptional(tag, OptionalFormat.F4))
1826             {
1827                 return new Optional<float>(readFloat());
1828             }
1829             else
1830             {
1831                 return new Optional<float>();
1832             }
1833         }
1834 
1835         /// <summary>
1836         /// Extracts an optional float value from the stream.
1837         /// </summary>
1838         /// <param name="tag">The numeric tag associated with the value.</param>
1839         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1840         /// <param name="v">The optional value.</param>
readFloat(int tag, out bool isset, out float v)1841         public void readFloat(int tag, out bool isset, out float v)
1842         {
1843             if(isset = readOptional(tag, OptionalFormat.F4))
1844             {
1845                 v = readFloat();
1846             }
1847             else
1848             {
1849                 v = 0;
1850             }
1851         }
1852 
1853         /// <summary>
1854         /// Extracts a sequence of float values from the stream.
1855         /// </summary>
1856         /// <returns>The extracted float sequence.</returns>
readFloatSeq()1857         public float[] readFloatSeq()
1858         {
1859             try
1860             {
1861                 int sz = readAndCheckSeqSize(4);
1862                 float[] v = new float[sz];
1863                 _buf.b.getFloatSeq(v);
1864                 return v;
1865             }
1866             catch(InvalidOperationException ex)
1867             {
1868                 throw new UnmarshalOutOfBoundsException(ex);
1869             }
1870         }
1871 
1872         /// <summary>
1873         /// Extracts a sequence of float values from the stream.
1874         /// </summary>
1875         /// <param name="l">The extracted float sequence as a list.</param>
readFloatSeq(out List<float> l)1876         public void readFloatSeq(out List<float> l)
1877         {
1878             //
1879             // Reading into an array and copy-constructing the
1880             // list is faster than constructing the list
1881             // and adding to it one element at a time.
1882             //
1883             l = new List<float>(readFloatSeq());
1884         }
1885 
1886         /// <summary>
1887         /// Extracts a sequence of float values from the stream.
1888         /// </summary>
1889         /// <param name="l">The extracted float sequence as a linked list.</param>
readFloatSeq(out LinkedList<float> l)1890         public void readFloatSeq(out LinkedList<float> l)
1891         {
1892             try
1893             {
1894                 int sz = readAndCheckSeqSize(4);
1895                 l = new LinkedList<float>();
1896                 for(int i = 0; i < sz; ++i)
1897                 {
1898                     l.AddLast(_buf.b.getFloat());
1899                 }
1900             }
1901             catch(InvalidOperationException ex)
1902             {
1903                 throw new UnmarshalOutOfBoundsException(ex);
1904             }
1905         }
1906 
1907         /// <summary>
1908         /// Extracts a sequence of float values from the stream.
1909         /// </summary>
1910         /// <param name="l">The extracted float sequence as a queue.</param>
readFloatSeq(out Queue<float> l)1911         public void readFloatSeq(out Queue<float> l)
1912         {
1913             //
1914             // Reading into an array and copy-constructing the
1915             // queue takes the same time as constructing the queue
1916             // and adding to it one element at a time, so
1917             // we avoid the copy.
1918             //
1919             try
1920             {
1921                 int sz = readAndCheckSeqSize(4);
1922                 l = new Queue<float>(sz);
1923                 for(int i = 0; i < sz; ++i)
1924                 {
1925                     l.Enqueue(_buf.b.getFloat());
1926                 }
1927             }
1928             catch(InvalidOperationException ex)
1929             {
1930                 throw new UnmarshalOutOfBoundsException(ex);
1931             }
1932         }
1933 
1934         /// <summary>
1935         /// Extracts a sequence of float values from the stream.
1936         /// </summary>
1937         /// <param name="l">The extracted float sequence as a stack.</param>
readFloatSeq(out Stack<float> l)1938         public void readFloatSeq(out Stack<float> l)
1939         {
1940             //
1941             // Reverse the contents by copying into an array first
1942             // because the stack is marshaled in top-to-bottom order.
1943             //
1944             float[] array = readFloatSeq();
1945             Array.Reverse(array);
1946             l = new Stack<float>(array);
1947         }
1948 
1949         /// <summary>
1950         /// Extracts an optional float sequence from the stream.
1951         /// </summary>
1952         /// <param name="tag">The numeric tag associated with the value.</param>
1953         /// <returns>The optional value.</returns>
readFloatSeq(int tag)1954         public Optional<float[]> readFloatSeq(int tag)
1955         {
1956             if(readOptional(tag, OptionalFormat.VSize))
1957             {
1958                 skipSize();
1959                 return new Optional<float[]>(readFloatSeq());
1960             }
1961             else
1962             {
1963                 return new Optional<float[]>();
1964             }
1965         }
1966 
1967         /// <summary>
1968         /// Extracts an optional float sequence from the stream.
1969         /// </summary>
1970         /// <param name="tag">The numeric tag associated with the value.</param>
1971         /// <param name="isset">True if the optional value is present, false otherwise.</param>
1972         /// <param name="v">The optional value.</param>
readFloatSeq(int tag, out bool isset, out float[] v)1973         public void readFloatSeq(int tag, out bool isset, out float[] v)
1974         {
1975             if(isset = readOptional(tag, OptionalFormat.VSize))
1976             {
1977                 skipSize();
1978                 v = readFloatSeq();
1979             }
1980             else
1981             {
1982                 v = null;
1983             }
1984         }
1985 
1986         /// <summary>
1987         /// Extracts a double value from the stream.
1988         /// </summary>
1989         /// <returns>The extracted double.</returns>
readDouble()1990         public double readDouble()
1991         {
1992             try
1993             {
1994                 return _buf.b.getDouble();
1995             }
1996             catch(InvalidOperationException ex)
1997             {
1998                 throw new UnmarshalOutOfBoundsException(ex);
1999             }
2000         }
2001 
2002         /// <summary>
2003         /// Extracts an optional double value from the stream.
2004         /// </summary>
2005         /// <param name="tag">The numeric tag associated with the value.</param>
2006         /// <returns>The optional value.</returns>
readDouble(int tag)2007         public Optional<double> readDouble(int tag)
2008         {
2009             if(readOptional(tag, OptionalFormat.F8))
2010             {
2011                 return new Optional<double>(readDouble());
2012             }
2013             else
2014             {
2015                 return new Optional<double>();
2016             }
2017         }
2018 
2019         /// <summary>
2020         /// Extracts an optional double value from the stream.
2021         /// </summary>
2022         /// <param name="tag">The numeric tag associated with the value.</param>
2023         /// <param name="isset">True if the optional value is present, false otherwise.</param>
2024         /// <param name="v">The optional value.</param>
readDouble(int tag, out bool isset, out double v)2025         public void readDouble(int tag, out bool isset, out double v)
2026         {
2027             if(isset = readOptional(tag, OptionalFormat.F8))
2028             {
2029                 v = readDouble();
2030             }
2031             else
2032             {
2033                 v = 0;
2034             }
2035         }
2036 
2037         /// <summary>
2038         /// Extracts a sequence of double values from the stream.
2039         /// </summary>
2040         /// <returns>The extracted double sequence.</returns>
readDoubleSeq()2041         public double[] readDoubleSeq()
2042         {
2043             try
2044             {
2045                 int sz = readAndCheckSeqSize(8);
2046                 double[] v = new double[sz];
2047                 _buf.b.getDoubleSeq(v);
2048                 return v;
2049             }
2050             catch(InvalidOperationException ex)
2051             {
2052                 throw new UnmarshalOutOfBoundsException(ex);
2053             }
2054         }
2055 
2056         /// <summary>
2057         /// Extracts a sequence of double values from the stream.
2058         /// </summary>
2059         /// <param name="l">The extracted double sequence as a list.</param>
readDoubleSeq(out List<double> l)2060         public void readDoubleSeq(out List<double> l)
2061         {
2062             //
2063             // Reading into an array and copy-constructing the
2064             // list is faster than constructing the list
2065             // and adding to it one element at a time.
2066             //
2067             l = new List<double>(readDoubleSeq());
2068         }
2069 
2070         /// <summary>
2071         /// Extracts a sequence of double values from the stream.
2072         /// </summary>
2073         /// <param name="l">The extracted double sequence as a linked list.</param>
readDoubleSeq(out LinkedList<double> l)2074         public void readDoubleSeq(out LinkedList<double> l)
2075         {
2076             try
2077             {
2078                 int sz = readAndCheckSeqSize(4);
2079                 l = new LinkedList<double>();
2080                 for(int i = 0; i < sz; ++i)
2081                 {
2082                     l.AddLast(_buf.b.getDouble());
2083                 }
2084             }
2085             catch(InvalidOperationException ex)
2086             {
2087                 throw new UnmarshalOutOfBoundsException(ex);
2088             }
2089         }
2090 
2091         /// <summary>
2092         /// Extracts a sequence of double values from the stream.
2093         /// </summary>
2094         /// <param name="l">The extracted double sequence as a queue.</param>
readDoubleSeq(out Queue<double> l)2095         public void readDoubleSeq(out Queue<double> l)
2096         {
2097             //
2098             // Reading into an array and copy-constructing the
2099             // queue takes the same time as constructing the queue
2100             // and adding to it one element at a time, so
2101             // we avoid the copy.
2102             //
2103             try
2104             {
2105                 int sz = readAndCheckSeqSize(4);
2106                 l = new Queue<double>(sz);
2107                 for(int i = 0; i < sz; ++i)
2108                 {
2109                     l.Enqueue(_buf.b.getDouble());
2110                 }
2111             }
2112             catch(InvalidOperationException ex)
2113             {
2114                 throw new UnmarshalOutOfBoundsException(ex);
2115             }
2116         }
2117 
2118         /// <summary>
2119         /// Extracts a sequence of double values from the stream.
2120         /// </summary>
2121         /// <param name="l">The extracted double sequence as a stack.</param>
readDoubleSeq(out Stack<double> l)2122         public void readDoubleSeq(out Stack<double> l)
2123         {
2124             //
2125             // Reverse the contents by copying into an array first
2126             // because the stack is marshaled in top-to-bottom order.
2127             //
2128             double[] array = readDoubleSeq();
2129             Array.Reverse(array);
2130             l = new Stack<double>(array);
2131         }
2132 
2133         /// <summary>
2134         /// Extracts an optional double sequence from the stream.
2135         /// </summary>
2136         /// <param name="tag">The numeric tag associated with the value.</param>
2137         /// <returns>The optional value.</returns>
readDoubleSeq(int tag)2138         public Optional<double[]> readDoubleSeq(int tag)
2139         {
2140             if(readOptional(tag, OptionalFormat.VSize))
2141             {
2142                 skipSize();
2143                 return new Optional<double[]>(readDoubleSeq());
2144             }
2145             else
2146             {
2147                 return new Optional<double[]>();
2148             }
2149         }
2150 
2151         /// <summary>
2152         /// Extracts an optional double sequence from the stream.
2153         /// </summary>
2154         /// <param name="tag">The numeric tag associated with the value.</param>
2155         /// <param name="isset">True if the optional value is present, false otherwise.</param>
2156         /// <param name="v">The optional value.</param>
readDoubleSeq(int tag, out bool isset, out double[] v)2157         public void readDoubleSeq(int tag, out bool isset, out double[] v)
2158         {
2159             if(isset = readOptional(tag, OptionalFormat.VSize))
2160             {
2161                 skipSize();
2162                 v = readDoubleSeq();
2163             }
2164             else
2165             {
2166                 v = null;
2167             }
2168         }
2169 
2170         private static System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(false, true);
2171 
2172         /// <summary>
2173         /// Extracts a string from the stream.
2174         /// </summary>
2175         /// <returns>The extracted string.</returns>
readString()2176         public string readString()
2177         {
2178             int len = readSize();
2179 
2180             if(len == 0)
2181             {
2182                 return "";
2183             }
2184 
2185             //
2186             // Check the buffer has enough bytes to read.
2187             //
2188             if(_buf.b.remaining() < len)
2189             {
2190                 throw new UnmarshalOutOfBoundsException();
2191             }
2192 
2193             try
2194             {
2195                 //
2196                 // We reuse the _stringBytes array to avoid creating
2197                 // excessive garbage
2198                 //
2199                 if(_stringBytes == null || len > _stringBytes.Length)
2200                 {
2201                     _stringBytes = new byte[len];
2202                 }
2203                 _buf.b.get(_stringBytes, 0, len);
2204                 return utf8.GetString(_stringBytes, 0, len);
2205             }
2206             catch(InvalidOperationException ex)
2207             {
2208                 throw new UnmarshalOutOfBoundsException(ex);
2209             }
2210             catch(ArgumentException ex)
2211             {
2212                 throw new MarshalException("Invalid UTF8 string", ex);
2213             }
2214         }
2215 
2216         /// <summary>
2217         /// Extracts an optional string from the stream.
2218         /// </summary>
2219         /// <param name="tag">The numeric tag associated with the value.</param>
2220         /// <returns>The optional value.</returns>
readString(int tag)2221         public Optional<string> readString(int tag)
2222         {
2223             if(readOptional(tag, OptionalFormat.VSize))
2224             {
2225                 return new Optional<string>(readString());
2226             }
2227             else
2228             {
2229                 return new Optional<string>();
2230             }
2231         }
2232 
2233         /// <summary>
2234         /// Extracts an optional string from the stream.
2235         /// </summary>
2236         /// <param name="tag">The numeric tag associated with the value.</param>
2237         /// <param name="isset">True if the optional value is present, false otherwise.</param>
2238         /// <param name="v">The optional value.</param>
readString(int tag, out bool isset, out string v)2239         public void readString(int tag, out bool isset, out string v)
2240         {
2241             if(isset = readOptional(tag, OptionalFormat.VSize))
2242             {
2243                 v = readString();
2244             }
2245             else
2246             {
2247                 v = null;
2248             }
2249         }
2250 
2251         /// <summary>
2252         /// Extracts a sequence of strings from the stream.
2253         /// </summary>
2254         /// <returns>The extracted string sequence.</returns>
readStringSeq()2255         public string[] readStringSeq()
2256         {
2257             int sz = readAndCheckSeqSize(1);
2258             string[] v = new string[sz];
2259             for(int i = 0; i < sz; i++)
2260             {
2261                 v[i] = readString();
2262             }
2263             return v;
2264         }
2265 
2266         /// <summary>
2267         /// Extracts a sequence of strings from the stream.
2268         /// </summary>
2269         /// <param name="l">The extracted string sequence as a list.</param>
readStringSeq(out List<string> l)2270         public void readStringSeq(out List<string> l)
2271         {
2272             //
2273             // Reading into an array and copy-constructing the
2274             // list is slower than constructing the list
2275             // and adding to it one element at a time.
2276             //
2277             int sz = readAndCheckSeqSize(1);
2278             l = new List<string>(sz);
2279             for(int i = 0; i < sz; ++i)
2280             {
2281                 l.Add(readString());
2282             }
2283         }
2284 
2285         /// <summary>
2286         /// Extracts a sequence of strings from the stream.
2287         /// </summary>
2288         /// <param name="l">The extracted string sequence as a linked list.</param>
readStringSeq(out LinkedList<string> l)2289         public void readStringSeq(out LinkedList<string> l)
2290         {
2291             //
2292             // Reading into an array and copy-constructing the
2293             // list is slower than constructing the list
2294             // and adding to it one element at a time.
2295             //
2296             int sz = readAndCheckSeqSize(1);
2297             l = new LinkedList<string>();
2298             for(int i = 0; i < sz; ++i)
2299             {
2300                 l.AddLast(readString());
2301             }
2302         }
2303 
2304         /// <summary>
2305         /// Extracts a sequence of strings from the stream.
2306         /// </summary>
2307         /// <param name="l">The extracted string sequence as a queue.</param>
readStringSeq(out Queue<string> l)2308         public void readStringSeq(out Queue<string> l)
2309         {
2310             //
2311             // Reading into an array and copy-constructing the
2312             // queue is slower than constructing the queue
2313             // and adding to it one element at a time.
2314             //
2315             int sz = readAndCheckSeqSize(1);
2316             l = new Queue<string>();
2317             for(int i = 0; i < sz; ++i)
2318             {
2319                 l.Enqueue(readString());
2320             }
2321         }
2322 
2323         /// <summary>
2324         /// Extracts a sequence of strings from the stream.
2325         /// </summary>
2326         /// <param name="l">The extracted string sequence as a stack.</param>
readStringSeq(out Stack<string> l)2327         public void readStringSeq(out Stack<string> l)
2328         {
2329             //
2330             // Reverse the contents by copying into an array first
2331             // because the stack is marshaled in top-to-bottom order.
2332             //
2333             string[] array = readStringSeq();
2334             Array.Reverse(array);
2335             l = new Stack<string>(array);
2336         }
2337 
2338         /// <summary>
2339         /// Extracts an optional string sequence from the stream.
2340         /// </summary>
2341         /// <param name="tag">The numeric tag associated with the value.</param>
2342         /// <returns>The optional value.</returns>
readStringSeq(int tag)2343         public Optional<string[]> readStringSeq(int tag)
2344         {
2345             if(readOptional(tag, OptionalFormat.FSize))
2346             {
2347                 skip(4);
2348                 return new Optional<string[]>(readStringSeq());
2349             }
2350             else
2351             {
2352                 return new Optional<string[]>();
2353             }
2354         }
2355 
2356         /// <summary>
2357         /// Extracts an optional string sequence from the stream.
2358         /// </summary>
2359         /// <param name="tag">The numeric tag associated with the value.</param>
2360         /// <param name="isset">True if the optional value is present, false otherwise.</param>
2361         /// <param name="v">The optional value.</param>
readStringSeq(int tag, out bool isset, out string[] v)2362         public void readStringSeq(int tag, out bool isset, out string[] v)
2363         {
2364             if(isset = readOptional(tag, OptionalFormat.FSize))
2365             {
2366                 skip(4);
2367                 v = readStringSeq();
2368             }
2369             else
2370             {
2371                 v = null;
2372             }
2373         }
2374 
2375         /// <summary>
2376         /// Extracts a proxy from the stream. The stream must have been initialized with a communicator.
2377         /// </summary>
2378         /// <returns>The extracted proxy.</returns>
readProxy()2379         public ObjectPrx readProxy()
2380         {
2381             return _instance.proxyFactory().streamToProxy(this);
2382         }
2383 
2384         /// <summary>
2385         /// Extracts an optional proxy from the stream. The stream must have been initialized with a communicator.
2386         /// </summary>
2387         /// <param name="tag">The numeric tag associated with the value.</param>
2388         /// <returns>The optional value.</returns>
readProxy(int tag)2389         public Optional<ObjectPrx> readProxy(int tag)
2390         {
2391             if(readOptional(tag, OptionalFormat.FSize))
2392             {
2393                 skip(4);
2394                 return new Optional<ObjectPrx>(readProxy());
2395             }
2396             else
2397             {
2398                 return new Optional<ObjectPrx>();
2399             }
2400         }
2401 
2402         /// <summary>
2403         /// Extracts an optional proxy from the stream. The stream must have been initialized with a communicator.
2404         /// </summary>
2405         /// <param name="tag">The numeric tag associated with the value.</param>
2406         /// <param name="isset">True if the optional value is present, false otherwise.</param>
2407         /// <param name="v">The optional value.</param>
readProxy(int tag, out bool isset, out ObjectPrx v)2408         public void readProxy(int tag, out bool isset, out ObjectPrx v)
2409         {
2410             if(isset = readOptional(tag, OptionalFormat.FSize))
2411             {
2412                 skip(4);
2413                 v = readProxy();
2414             }
2415             else
2416             {
2417                 v = null;
2418             }
2419         }
2420 
2421         /// <summary>
2422         /// Read an enumerated value.
2423         /// </summary>
2424         /// <param name="maxValue">The maximum enumerator value in the definition.</param>
2425         /// <returns>The enumerator.</returns>
readEnum(int maxValue)2426         public int readEnum(int maxValue)
2427         {
2428             if(getEncoding().Equals(Util.Encoding_1_0))
2429             {
2430                 if(maxValue < 127)
2431                 {
2432                     return readByte();
2433                 }
2434                 else if(maxValue < 32767)
2435                 {
2436                     return readShort();
2437                 }
2438                 else
2439                 {
2440                     return readInt();
2441                 }
2442             }
2443             else
2444             {
2445                 return readSize();
2446             }
2447         }
2448 
2449         /// <summary>
2450         /// Extracts the index of a Slice value from the stream.
2451         /// </summary>
2452         /// <param name="cb">The callback to notify the application when the extracted instance is available.
2453         /// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
2454         /// corresponding instance has been fully unmarshaled.</param>
2455         public void readValue<T>(System.Action<T> cb) where T : Value
2456         {
2457             readValue(v => {
2458                 if(v == null || v is T)
2459                 {
2460                     cb((T)v);
2461                 }
2462                 else
2463                 {
2464                     IceInternal.Ex.throwUOE(typeof(T), v);
2465                 }
2466             });
2467         }
2468 
2469         /// <summary>
2470         /// Extracts the index of a Slice value from the stream.
2471         /// </summary>
2472         /// <param name="cb">The callback to notify the application when the extracted instance is available.
2473         /// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
2474         /// corresponding instance has been fully unmarshaled.</param>
readValue(System.Action<Value> cb)2475         public void readValue(System.Action<Value> cb)
2476         {
2477             initEncaps();
2478             _encapsStack.decoder.readValue(cb);
2479         }
2480 
2481         /// <summary>
2482         /// Extracts the index of an optional Slice value from the stream.
2483         /// </summary>
2484         /// <param name="tag">The numeric tag associated with the value.</param>
2485         /// <param name="cb">The callback to notify the application when the extracted instance is available (if any).
2486         /// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
2487         /// corresponding instance has been fully unmarshaled.</param>
2488         public void readValue<T>(int tag, System.Action<T> cb) where T : Value
2489         {
2490             readValue(tag, v => {
2491                 if(v == null || v is T)
2492                 {
2493                     cb((T)v);
2494                 }
2495                 else
2496                 {
2497                     IceInternal.Ex.throwUOE(typeof(T), v);
2498                 }
2499             });
2500         }
2501 
2502         /// <summary>
2503         /// Extracts the index of an optional Slice value from the stream.
2504         /// </summary>
2505         /// <param name="tag">The numeric tag associated with the value.</param>
2506         /// <param name="cb">The callback to notify the application when the extracted instance is available (if any).
2507         /// The stream extracts Slice values in stages. The Ice run time invokes the delegate when the
2508         /// corresponding instance has been fully unmarshaled.</param>
readValue(int tag, System.Action<Value> cb)2509         public void readValue(int tag, System.Action<Value> cb)
2510         {
2511             if(readOptional(tag, OptionalFormat.Class))
2512             {
2513                 readValue(cb);
2514             }
2515         }
2516 
2517         /// <summary>
2518         /// Extracts a user exception from the stream and throws it.
2519         /// </summary>
throwException()2520         public void throwException()
2521         {
2522             throwException(null);
2523         }
2524 
2525         /// <summary>
2526         /// Extracts a user exception from the stream and throws it.
2527         /// </summary>
2528         /// <param name="factory">The user exception factory, or null to use the stream's default behavior.</param>
throwException(UserExceptionFactory factory)2529         public void throwException(UserExceptionFactory factory)
2530         {
2531             initEncaps();
2532             _encapsStack.decoder.throwException(factory);
2533         }
2534 
2535         /// <summary>
2536         /// Skip the given number of bytes.
2537         /// </summary>
2538         /// <param name="size">The number of bytes to skip</param>
skip(int size)2539         public void skip(int size)
2540         {
2541             if(size < 0 || size > _buf.b.remaining())
2542             {
2543                 throw new UnmarshalOutOfBoundsException();
2544             }
2545             _buf.b.position(_buf.b.position() + size);
2546         }
2547 
2548         /// <summary>
2549         /// Skip over a size value.
2550         /// </summary>
skipSize()2551         public void skipSize()
2552         {
2553             byte b = readByte();
2554             if(b == 255)
2555             {
2556                 skip(4);
2557             }
2558         }
2559 
2560         /// <summary>
2561         /// Determines the current position in the stream.
2562         /// </summary>
2563         /// <returns>The current position.</returns>
pos()2564         public int pos()
2565         {
2566             return _buf.b.position();
2567         }
2568 
2569         /// <summary>
2570         /// Sets the current position in the stream.
2571         /// </summary>
2572         /// <param name="n">The new position.</param>
pos(int n)2573         public void pos(int n)
2574         {
2575             _buf.b.position(n);
2576         }
2577 
2578         /// <summary>
2579         /// Determines the current size of the stream.
2580         /// </summary>
2581         /// <returns>The current size.</returns>
size()2582         public int size()
2583         {
2584             return _buf.size();
2585         }
2586 
2587         /// <summary>
2588         /// Determines whether the stream is empty.
2589         /// </summary>
2590         /// <returns>True if the internal buffer has no data, false otherwise.</returns>
isEmpty()2591         public bool isEmpty()
2592         {
2593             return _buf.empty();
2594         }
2595 
readOptImpl(int readTag, OptionalFormat expectedFormat)2596         private bool readOptImpl(int readTag, OptionalFormat expectedFormat)
2597         {
2598             if(isEncoding_1_0())
2599             {
2600                 return false; // Optional members aren't supported with the 1.0 encoding.
2601             }
2602 
2603             while(true)
2604             {
2605                 if(_buf.b.position() >= _encapsStack.start + _encapsStack.sz)
2606                 {
2607                     return false; // End of encapsulation also indicates end of optionals.
2608                 }
2609 
2610                 int v = readByte();
2611                 if(v == Protocol.OPTIONAL_END_MARKER)
2612                 {
2613                     _buf.b.position(_buf.b.position() - 1); // Rewind.
2614                     return false;
2615                 }
2616 
2617                 OptionalFormat format = (OptionalFormat)(v & 0x07); // First 3 bits.
2618                 int tag = v >> 3;
2619                 if(tag == 30)
2620                 {
2621                     tag = readSize();
2622                 }
2623 
2624                 if(tag > readTag)
2625                 {
2626                     int offset = tag < 30 ? 1 : (tag < 255 ? 2 : 6); // Rewind
2627                     _buf.b.position(_buf.b.position() - offset);
2628                     return false; // No optional data members with the requested tag.
2629                 }
2630                 else if(tag < readTag)
2631                 {
2632                     skipOptional(format); // Skip optional data members
2633                 }
2634                 else
2635                 {
2636                     if(format != expectedFormat)
2637                     {
2638                         throw new MarshalException("invalid optional data member `" + tag + "': unexpected format");
2639                     }
2640                     return true;
2641                 }
2642             }
2643         }
2644 
skipOptional(OptionalFormat format)2645         private void skipOptional(OptionalFormat format)
2646         {
2647             switch(format)
2648             {
2649             case OptionalFormat.F1:
2650             {
2651                 skip(1);
2652                 break;
2653             }
2654             case OptionalFormat.F2:
2655             {
2656                 skip(2);
2657                 break;
2658             }
2659             case OptionalFormat.F4:
2660             {
2661                 skip(4);
2662                 break;
2663             }
2664             case OptionalFormat.F8:
2665             {
2666                 skip(8);
2667                 break;
2668             }
2669             case OptionalFormat.Size:
2670             {
2671                 skipSize();
2672                 break;
2673             }
2674             case OptionalFormat.VSize:
2675             {
2676                 skip(readSize());
2677                 break;
2678             }
2679             case OptionalFormat.FSize:
2680             {
2681                 skip(readInt());
2682                 break;
2683             }
2684             case OptionalFormat.Class:
2685             {
2686                 readValue(null);
2687                 break;
2688             }
2689             }
2690         }
2691 
skipOptionals()2692         private bool skipOptionals()
2693         {
2694             //
2695             // Skip remaining un-read optional members.
2696             //
2697             while(true)
2698             {
2699                 if(_buf.b.position() >= _encapsStack.start + _encapsStack.sz)
2700                 {
2701                     return false; // End of encapsulation also indicates end of optionals.
2702                 }
2703 
2704                 int v = readByte();
2705                 if(v == Protocol.OPTIONAL_END_MARKER)
2706                 {
2707                     return true;
2708                 }
2709 
2710                 OptionalFormat format = (OptionalFormat)(v & 0x07); // Read first 3 bits.
2711                 if((v >> 3) == 30)
2712                 {
2713                     skipSize();
2714                 }
2715                 skipOptional(format);
2716             }
2717         }
2718 
createUserException(string id)2719         private UserException createUserException(string id)
2720         {
2721             UserException userEx = null;
2722 
2723             try
2724             {
2725                 if(_classResolver != null)
2726                 {
2727                     Type c = _classResolver(id);
2728                     if(c != null)
2729                     {
2730                         Debug.Assert(!c.IsAbstract && !c.IsInterface);
2731                         userEx = (UserException)IceInternal.AssemblyUtil.createInstance(c);
2732                     }
2733                 }
2734             }
2735             catch(Exception ex)
2736             {
2737                 throw new MarshalException(ex);
2738             }
2739 
2740             return userEx;
2741         }
2742 
2743         private IceInternal.Instance _instance;
2744         private IceInternal.Buffer _buf;
2745         private object _closure;
2746         private byte[] _stringBytes; // Reusable array for reading strings.
2747 
2748         private enum SliceType { NoSlice, ValueSlice, ExceptionSlice }
2749 
2750         abstract private class EncapsDecoder
2751         {
2752             protected struct PatchEntry
2753             {
PatchEntryIce.InputStream.EncapsDecoder.PatchEntry2754                 public PatchEntry(System.Action<Value> cb, int classGraphDepth)
2755                 {
2756                     this.cb = cb;
2757                     this.classGraphDepth = classGraphDepth;
2758                 }
2759 
2760                 public System.Action<Value> cb;
2761                 public int classGraphDepth;
2762             };
2763 
EncapsDecoder(InputStream stream, Encaps encaps, bool sliceValues, int classGraphDepthMax, ValueFactoryManager f, System.Func<string, Type> cr)2764             internal EncapsDecoder(InputStream stream, Encaps encaps, bool sliceValues,
2765                                    int classGraphDepthMax, ValueFactoryManager f,
2766                                    System.Func<string, Type> cr)
2767             {
2768                 _stream = stream;
2769                 _encaps = encaps;
2770                 _sliceValues = sliceValues;
2771                 _classGraphDepthMax = classGraphDepthMax;
2772                 _classGraphDepth = 0;
2773                 _valueFactoryManager = f;
2774                 _classResolver = cr;
2775                 _typeIdIndex = 0;
2776                 _unmarshaledMap = new Dictionary<int, Value>();
2777             }
2778 
readValue(System.Action<Value> cb)2779             internal abstract void readValue(System.Action<Value> cb);
throwException(UserExceptionFactory factory)2780             internal abstract void throwException(UserExceptionFactory factory);
2781 
startInstance(SliceType type)2782             internal abstract void startInstance(SliceType type);
endInstance(bool preserve)2783             internal abstract SlicedData endInstance(bool preserve);
startSlice()2784             internal abstract string startSlice();
endSlice()2785             internal abstract void endSlice();
skipSlice()2786             internal abstract void skipSlice();
2787 
readOptional(int tag, OptionalFormat format)2788             internal virtual bool readOptional(int tag, OptionalFormat format)
2789             {
2790                 return false;
2791             }
2792 
readPendingValues()2793             internal virtual void readPendingValues()
2794             {
2795             }
2796 
readTypeId(bool isIndex)2797             protected string readTypeId(bool isIndex)
2798             {
2799                 if(_typeIdMap == null)
2800                 {
2801                     _typeIdMap = new Dictionary<int, string>();
2802                 }
2803 
2804                 if(isIndex)
2805                 {
2806                     int index = _stream.readSize();
2807                     string typeId;
2808                     if(!_typeIdMap.TryGetValue(index, out typeId))
2809                     {
2810                         throw new UnmarshalOutOfBoundsException();
2811                     }
2812                     return typeId;
2813                 }
2814                 else
2815                 {
2816                     string typeId = _stream.readString();
2817                     _typeIdMap.Add(++_typeIdIndex, typeId);
2818                     return typeId;
2819                 }
2820             }
2821 
resolveClass(string typeId)2822             protected Type resolveClass(string typeId)
2823             {
2824                 Type cls = null;
2825                 if(_typeIdCache == null)
2826                 {
2827                     _typeIdCache = new Dictionary<string, Type>(); // Lazy initialization.
2828                 }
2829                 else
2830                 {
2831                     _typeIdCache.TryGetValue(typeId, out cls);
2832                 }
2833 
2834                 if(cls == typeof(EncapsDecoder)) // Marker for non-existent class.
2835                 {
2836                     cls = null;
2837                 }
2838                 else if(cls == null)
2839                 {
2840                     try
2841                     {
2842                         if(_classResolver != null)
2843                         {
2844                             cls = _classResolver(typeId);
2845                             _typeIdCache.Add(typeId, cls != null ? cls : typeof(EncapsDecoder));
2846                         }
2847                     }
2848                     catch(Exception ex)
2849                     {
2850                         throw new NoValueFactoryException("no value factory", typeId, ex);
2851                     }
2852                 }
2853 
2854                 return cls;
2855             }
2856 
newInstance(string typeId)2857             protected Value newInstance(string typeId)
2858             {
2859                 //
2860                 // Try to find a factory registered for the specific type.
2861                 //
2862                 var userFactory = _valueFactoryManager.find(typeId);
2863                 Value v = null;
2864                 if(userFactory != null)
2865                 {
2866                     v = userFactory(typeId);
2867                 }
2868 
2869                 //
2870                 // If that fails, invoke the default factory if one has been
2871                 // registered.
2872                 //
2873                 if(v == null)
2874                 {
2875                     userFactory = _valueFactoryManager.find("");
2876                     if(userFactory != null)
2877                     {
2878                         v = userFactory(typeId);
2879                     }
2880                 }
2881 
2882                 //
2883                 // Last chance: try to instantiate the class dynamically.
2884                 //
2885                 if(v == null)
2886                 {
2887                     Type cls = resolveClass(typeId);
2888 
2889                     if(cls != null)
2890                     {
2891                         try
2892                         {
2893                             Debug.Assert(!cls.IsAbstract && !cls.IsInterface);
2894                             v = (Value)IceInternal.AssemblyUtil.createInstance(cls);
2895                         }
2896                         catch(Exception ex)
2897                         {
2898                             throw new NoValueFactoryException("no value factory", typeId, ex);
2899                         }
2900                     }
2901                 }
2902 
2903                 return v;
2904             }
2905 
addPatchEntry(int index, System.Action<Value> cb)2906             protected void addPatchEntry(int index, System.Action<Value> cb)
2907             {
2908                 Debug.Assert(index > 0);
2909 
2910                 //
2911                 // Check if we already unmarshaled the instance. If that's the case,
2912                 // just call the callback and we're done.
2913                 //
2914                 Value obj;
2915                 if(_unmarshaledMap.TryGetValue(index, out obj))
2916                 {
2917                     cb(obj);
2918                     return;
2919                 }
2920 
2921                 if(_patchMap == null)
2922                 {
2923                     _patchMap = new Dictionary<int, LinkedList<PatchEntry>>();
2924                 }
2925 
2926                 //
2927                 // Add patch entry if the instance isn't unmarshaled yet,
2928                 // the callback will be called when the instance is
2929                 // unmarshaled.
2930                 //
2931                 LinkedList<PatchEntry> l;
2932                 if(!_patchMap.TryGetValue(index, out l))
2933                 {
2934                     //
2935                     // We have no outstanding instances to be patched for this
2936                     // index, so make a new entry in the patch map.
2937                     //
2938                     l = new LinkedList<PatchEntry>();
2939                     _patchMap.Add(index, l);
2940                 }
2941 
2942                 //
2943                 // Append a patch entry for this instance.
2944                 //
2945                 l.AddLast(new PatchEntry(cb, _classGraphDepth));
2946             }
2947 
unmarshal(int index, Value v)2948             protected void unmarshal(int index, Value v)
2949             {
2950                 //
2951                 // Add the instance to the map of unmarshaled instances, this must
2952                 // be done before reading the instances (for circular references).
2953                 //
2954                 _unmarshaledMap.Add(index, v);
2955 
2956                 //
2957                 // Read the instance.
2958                 //
2959                 v.iceRead(_stream);
2960 
2961                 if(_patchMap != null)
2962                 {
2963                     //
2964                     // Patch all instances now that the instance is unmarshaled.
2965                     //
2966                     LinkedList<PatchEntry> l;
2967                     if(_patchMap.TryGetValue(index, out l))
2968                     {
2969                         Debug.Assert(l.Count > 0);
2970 
2971                         //
2972                         // Patch all pointers that refer to the instance.
2973                         //
2974                         foreach(PatchEntry entry in l)
2975                         {
2976                             entry.cb(v);
2977                         }
2978 
2979                         //
2980                         // Clear out the patch map for that index -- there is nothing left
2981                         // to patch for that index for the time being.
2982                         //
2983                         _patchMap.Remove(index);
2984                     }
2985                 }
2986 
2987                 if((_patchMap == null || _patchMap.Count == 0) && _valueList == null)
2988                 {
2989                     try
2990                     {
2991                         v.ice_postUnmarshal();
2992                     }
2993                     catch(System.Exception ex)
2994                     {
2995                         string s = "exception raised by ice_postUnmarshal:\n" + ex;
2996                         _stream.instance().initializationData().logger.warning(s);
2997                     }
2998                 }
2999                 else
3000                 {
3001                     if(_valueList == null)
3002                     {
3003                         _valueList = new List<Value>();
3004                     }
3005                     _valueList.Add(v);
3006 
3007                     if(_patchMap == null || _patchMap.Count == 0)
3008                     {
3009                         //
3010                         // Iterate over the instance list and invoke ice_postUnmarshal on
3011                         // each instance. We must do this after all instances have been
3012                         // unmarshaled in order to ensure that any instance data members
3013                         // have been properly patched.
3014                         //
3015                         foreach(var p in _valueList)
3016                         {
3017                             try
3018                             {
3019                                 p.ice_postUnmarshal();
3020                             }
3021                             catch(System.Exception ex)
3022                             {
3023                                 string s = "exception raised by ice_postUnmarshal:\n" + ex;
3024                                 _stream.instance().initializationData().logger.warning(s);
3025                             }
3026                         }
3027                         _valueList.Clear();
3028                     }
3029                 }
3030             }
3031 
3032             protected readonly InputStream _stream;
3033             protected readonly Encaps _encaps;
3034             protected readonly bool _sliceValues;
3035             protected readonly int _classGraphDepthMax;
3036             protected int _classGraphDepth;
3037             protected ValueFactoryManager _valueFactoryManager;
3038             protected System.Func<string, Type> _classResolver;
3039 
3040             //
3041             // Encapsulation attributes for object unmarshaling.
3042             //
3043             protected Dictionary<int, LinkedList<PatchEntry>> _patchMap;
3044             private Dictionary<int, Value> _unmarshaledMap;
3045             private Dictionary<int, string> _typeIdMap;
3046             private int _typeIdIndex;
3047             private List<Value> _valueList;
3048             private Dictionary<string, Type> _typeIdCache;
3049         }
3050 
3051         private sealed class EncapsDecoder10 : EncapsDecoder
3052         {
EncapsDecoder10(InputStream stream, Encaps encaps, bool sliceValues, int classGraphDepthMax, ValueFactoryManager f, System.Func<string, Type> cr)3053             internal EncapsDecoder10(InputStream stream, Encaps encaps, bool sliceValues, int classGraphDepthMax,
3054                                      ValueFactoryManager f, System.Func<string, Type> cr)
3055                 : base(stream, encaps, sliceValues, classGraphDepthMax, f, cr)
3056             {
3057                 _sliceType = SliceType.NoSlice;
3058             }
3059 
readValue(System.Action<Value> cb)3060             internal override void readValue(System.Action<Value> cb)
3061             {
3062                 Debug.Assert(cb != null);
3063 
3064                 //
3065                 // Object references are encoded as a negative integer in 1.0.
3066                 //
3067                 int index = _stream.readInt();
3068                 if(index > 0)
3069                 {
3070                     throw new MarshalException("invalid object id");
3071                 }
3072                 index = -index;
3073 
3074                 if(index == 0)
3075                 {
3076                     cb(null);
3077                 }
3078                 else
3079                 {
3080                     addPatchEntry(index, cb);
3081                 }
3082             }
3083 
throwException(UserExceptionFactory factory)3084             internal override void throwException(UserExceptionFactory factory)
3085             {
3086                 Debug.Assert(_sliceType == SliceType.NoSlice);
3087 
3088                 //
3089                 // User exception with the 1.0 encoding start with a bool flag
3090                 // that indicates whether or not the exception has classes.
3091                 //
3092                 // This allows reading the pending instances even if some part of
3093                 // the exception was sliced.
3094                 //
3095                 bool usesClasses = _stream.readBool();
3096 
3097                 _sliceType = SliceType.ExceptionSlice;
3098                 _skipFirstSlice = false;
3099 
3100                 //
3101                 // Read the first slice header.
3102                 //
3103                 startSlice();
3104                 string mostDerivedId = _typeId;
3105                 while(true)
3106                 {
3107                     UserException userEx = null;
3108 
3109                     //
3110                     // Use a factory if one was provided.
3111                     //
3112                     if(factory != null)
3113                     {
3114                         try
3115                         {
3116                             factory(_typeId);
3117                         }
3118                         catch(UserException ex)
3119                         {
3120                             userEx = ex;
3121                         }
3122                     }
3123 
3124                     if(userEx == null)
3125                     {
3126                         userEx = _stream.createUserException(_typeId);
3127                     }
3128 
3129                     //
3130                     // We found the exception.
3131                     //
3132                     if(userEx != null)
3133                     {
3134                         userEx.iceRead(_stream);
3135                         if(usesClasses)
3136                         {
3137                             readPendingValues();
3138                         }
3139                         throw userEx;
3140 
3141                         // Never reached.
3142                     }
3143 
3144                     //
3145                     // Slice off what we don't understand.
3146                     //
3147                     skipSlice();
3148                     try
3149                     {
3150                         startSlice();
3151                     }
3152                     catch(UnmarshalOutOfBoundsException ex)
3153                     {
3154                         //
3155                         // An oversight in the 1.0 encoding means there is no marker to indicate
3156                         // the last slice of an exception. As a result, we just try to read the
3157                         // next type ID, which raises UnmarshalOutOfBoundsException when the
3158                         // input buffer underflows.
3159                         //
3160                         // Set the reason member to a more helpful message.
3161                         //
3162                         ex.reason = "unknown exception type `" + mostDerivedId + "'";
3163                         throw;
3164                     }
3165                 }
3166             }
3167 
startInstance(SliceType sliceType)3168             internal override void startInstance(SliceType sliceType)
3169             {
3170                 Debug.Assert(_sliceType == sliceType);
3171                 _skipFirstSlice = true;
3172             }
3173 
endInstance(bool preserve)3174             internal override SlicedData endInstance(bool preserve)
3175             {
3176                 //
3177                 // Read the Ice::Object slice.
3178                 //
3179                 if(_sliceType == SliceType.ValueSlice)
3180                 {
3181                     startSlice();
3182                     int sz = _stream.readSize(); // For compatibility with the old AFM.
3183                     if(sz != 0)
3184                     {
3185                         throw new MarshalException("invalid Object slice");
3186                     }
3187                     endSlice();
3188                 }
3189 
3190                 _sliceType = SliceType.NoSlice;
3191                 return null;
3192             }
3193 
startSlice()3194             internal override string startSlice()
3195             {
3196                 //
3197                 // If first slice, don't read the header, it was already read in
3198                 // readInstance or throwException to find the factory.
3199                 //
3200                 if(_skipFirstSlice)
3201                 {
3202                     _skipFirstSlice = false;
3203                     return _typeId;
3204                 }
3205 
3206                 //
3207                 // For instances, first read the type ID bool which indicates
3208                 // whether or not the type ID is encoded as a string or as an
3209                 // index. For exceptions, the type ID is always encoded as a
3210                 // string.
3211                 //
3212                 if(_sliceType == SliceType.ValueSlice) // For exceptions, the type ID is always encoded as a string
3213                 {
3214                     bool isIndex = _stream.readBool();
3215                     _typeId = readTypeId(isIndex);
3216                 }
3217                 else
3218                 {
3219                     _typeId = _stream.readString();
3220                 }
3221 
3222                 _sliceSize = _stream.readInt();
3223                 if(_sliceSize < 4)
3224                 {
3225                     throw new UnmarshalOutOfBoundsException();
3226                 }
3227 
3228                 return _typeId;
3229             }
3230 
endSlice()3231             internal override void endSlice()
3232             {
3233             }
3234 
skipSlice()3235             internal override void skipSlice()
3236             {
3237                 if(_stream.instance().traceLevels().slicing > 0)
3238                 {
3239                     Logger logger = _stream.instance().initializationData().logger;
3240                     string slicingCat = _stream.instance().traceLevels().slicingCat;
3241                     if(_sliceType == SliceType.ValueSlice)
3242                     {
3243                         IceInternal.TraceUtil.traceSlicing("object", _typeId, slicingCat, logger);
3244                     }
3245                     else
3246                     {
3247                         IceInternal.TraceUtil.traceSlicing("exception", _typeId, slicingCat, logger);
3248                     }
3249                 }
3250 
3251                 Debug.Assert(_sliceSize >= 4);
3252                 _stream.skip(_sliceSize - 4);
3253             }
3254 
readPendingValues()3255             internal override void readPendingValues()
3256             {
3257                 int num;
3258                 do
3259                 {
3260                     num = _stream.readSize();
3261                     for(int k = num; k > 0; --k)
3262                     {
3263                         readInstance();
3264                     }
3265                 }
3266                 while(num > 0);
3267 
3268                 if(_patchMap != null && _patchMap.Count > 0)
3269                 {
3270                     //
3271                     // If any entries remain in the patch map, the sender has sent an index for an instance, but failed
3272                     // to supply the instance.
3273                     //
3274                     throw new MarshalException("index for class received, but no instance");
3275                 }
3276             }
3277 
readInstance()3278             private void readInstance()
3279             {
3280                 int index = _stream.readInt();
3281 
3282                 if(index <= 0)
3283                 {
3284                     throw new MarshalException("invalid object id");
3285                 }
3286 
3287                 _sliceType = SliceType.ValueSlice;
3288                 _skipFirstSlice = false;
3289 
3290                 //
3291                 // Read the first slice header.
3292                 //
3293                 startSlice();
3294                 string mostDerivedId = _typeId;
3295                 Value v = null;
3296                 while(true)
3297                 {
3298                     //
3299                     // For the 1.0 encoding, the type ID for the base Object class
3300                     // marks the last slice.
3301                     //
3302                     if(_typeId.Equals(Value.ice_staticId()))
3303                     {
3304                         throw new NoValueFactoryException("", mostDerivedId);
3305                     }
3306 
3307                     v = newInstance(_typeId);
3308 
3309                     //
3310                     // We found a factory, we get out of this loop.
3311                     //
3312                     if(v != null)
3313                     {
3314                         break;
3315                     }
3316 
3317                     //
3318                     // If slicing is disabled, stop unmarshaling.
3319                     //
3320                     if(!_sliceValues)
3321                     {
3322                         throw new NoValueFactoryException("no value factory found and slicing is disabled", _typeId);
3323                     }
3324 
3325                     //
3326                     // Slice off what we don't understand.
3327                     //
3328                     skipSlice();
3329                     startSlice(); // Read next Slice header for next iteration.
3330                 }
3331 
3332                 //
3333                 // Compute the biggest class graph depth of this object. To compute this,
3334                 // we get the class graph depth of each ancestor from the patch map and
3335                 // keep the biggest one.
3336                 //
3337                 _classGraphDepth = 0;
3338                 LinkedList<PatchEntry> l;
3339                 if(_patchMap != null && _patchMap.TryGetValue(index, out l))
3340                 {
3341                     Debug.Assert(l.Count > 0);
3342                     foreach(PatchEntry entry in l)
3343                     {
3344                         if(entry.classGraphDepth > _classGraphDepth)
3345                         {
3346                             _classGraphDepth = entry.classGraphDepth;
3347                         }
3348                     }
3349                 }
3350 
3351                 if(++_classGraphDepth > _classGraphDepthMax)
3352                 {
3353                     throw new MarshalException("maximum class graph depth reached");
3354                 }
3355 
3356                 //
3357                 // Unmarshal the instance and add it to the map of unmarshaled instances.
3358                 //
3359                 unmarshal(index, v);
3360             }
3361 
3362             // Object/exception attributes
3363             private SliceType _sliceType;
3364             private bool _skipFirstSlice;
3365 
3366             // Slice attributes
3367             private int _sliceSize;
3368             private string _typeId;
3369         }
3370 
3371         private sealed class EncapsDecoder11 : EncapsDecoder
3372         {
EncapsDecoder11(InputStream stream, Encaps encaps, bool sliceValues, int classGraphDepthMax, ValueFactoryManager f, System.Func<string, Type> cr, System.Func<int, string> r)3373             internal EncapsDecoder11(InputStream stream, Encaps encaps, bool sliceValues, int classGraphDepthMax,
3374                                      ValueFactoryManager f, System.Func<string, Type> cr, System.Func<int, string> r)
3375                 : base(stream, encaps, sliceValues, classGraphDepthMax, f, cr)
3376             {
3377                 _compactIdResolver = r;
3378                 _current = null;
3379                 _valueIdIndex = 1;
3380             }
3381 
readValue(System.Action<Value> cb)3382             internal override void readValue(System.Action<Value> cb)
3383             {
3384                 int index = _stream.readSize();
3385                 if(index < 0)
3386                 {
3387                     throw new MarshalException("invalid object id");
3388                 }
3389                 else if(index == 0)
3390                 {
3391                     if(cb != null)
3392                     {
3393                         cb(null);
3394                     }
3395                 }
3396                 else if(_current != null && (_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
3397                 {
3398                     //
3399                     // When reading an instance within a slice and there's an
3400                     // indirect instance table, always read an indirect reference
3401                     // that points to an instance from the indirect instance table
3402                     // marshaled at the end of the Slice.
3403                     //
3404                     // Maintain a list of indirect references. Note that the
3405                     // indirect index starts at 1, so we decrement it by one to
3406                     // derive an index into the indirection table that we'll read
3407                     // at the end of the slice.
3408                     //
3409                     if(cb != null)
3410                     {
3411                         if(_current.indirectPatchList == null)
3412                         {
3413                             _current.indirectPatchList = new Stack<IndirectPatchEntry>();
3414                         }
3415                         IndirectPatchEntry e = new IndirectPatchEntry();
3416                         e.index = index - 1;
3417                         e.patcher = cb;
3418                         _current.indirectPatchList.Push(e);
3419                     }
3420                 }
3421                 else
3422                 {
3423                     readInstance(index, cb);
3424                 }
3425             }
3426 
throwException(UserExceptionFactory factory)3427             internal override void throwException(UserExceptionFactory factory)
3428             {
3429                 Debug.Assert(_current == null);
3430 
3431                 push(SliceType.ExceptionSlice);
3432 
3433                 //
3434                 // Read the first slice header.
3435                 //
3436                 startSlice();
3437                 string mostDerivedId = _current.typeId;
3438                 while(true)
3439                 {
3440                     UserException userEx = null;
3441 
3442                     //
3443                     // Use a factory if one was provided.
3444                     //
3445                     if(factory != null)
3446                     {
3447                         try
3448                         {
3449                             factory(_current.typeId);
3450                         }
3451                         catch(UserException ex)
3452                         {
3453                             userEx = ex;
3454                         }
3455                     }
3456 
3457                     if(userEx == null)
3458                     {
3459                         userEx = _stream.createUserException(_current.typeId);
3460                     }
3461 
3462                     //
3463                     // We found the exception.
3464                     //
3465                     if(userEx != null)
3466                     {
3467                         userEx.iceRead(_stream);
3468                         throw userEx;
3469 
3470                         // Never reached.
3471                     }
3472 
3473                     //
3474                     // Slice off what we don't understand.
3475                     //
3476                     skipSlice();
3477 
3478                     if((_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0)
3479                     {
3480                         if(mostDerivedId.StartsWith("::", StringComparison.Ordinal))
3481                         {
3482                             throw new UnknownUserException(mostDerivedId.Substring(2));
3483                         }
3484                         else
3485                         {
3486                             throw new UnknownUserException(mostDerivedId);
3487                         }
3488                     }
3489 
3490                     startSlice();
3491                 }
3492             }
3493 
startInstance(SliceType sliceType)3494             internal override void startInstance(SliceType sliceType)
3495             {
3496                 Debug.Assert(_current.sliceType == sliceType);
3497                 _current.skipFirstSlice = true;
3498             }
3499 
endInstance(bool preserve)3500             internal override SlicedData endInstance(bool preserve)
3501             {
3502                 SlicedData slicedData = null;
3503                 if(preserve)
3504                 {
3505                     slicedData = readSlicedData();
3506                 }
3507                 if(_current.slices != null)
3508                 {
3509                     _current.slices.Clear();
3510                     _current.indirectionTables.Clear();
3511                 }
3512                 _current = _current.previous;
3513                 return slicedData;
3514             }
3515 
startSlice()3516             internal override string startSlice()
3517             {
3518                 //
3519                 // If first slice, don't read the header, it was already read in
3520                 // readInstance or throwException to find the factory.
3521                 //
3522                 if(_current.skipFirstSlice)
3523                 {
3524                     _current.skipFirstSlice = false;
3525                     return _current.typeId;
3526                 }
3527 
3528                 _current.sliceFlags = _stream.readByte();
3529 
3530                 //
3531                 // Read the type ID, for instance slices the type ID is encoded as a
3532                 // string or as an index, for exceptions it's always encoded as a
3533                 // string.
3534                 //
3535                 if(_current.sliceType == SliceType.ValueSlice)
3536                 {
3537                     //
3538                     // Must be checked first!
3539                     //
3540                     if((_current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_COMPACT) == Protocol.FLAG_HAS_TYPE_ID_COMPACT)
3541                     {
3542                         _current.typeId = "";
3543                         _current.compactId = _stream.readSize();
3544                     }
3545                     else if((_current.sliceFlags &
3546                             (Protocol.FLAG_HAS_TYPE_ID_INDEX | Protocol.FLAG_HAS_TYPE_ID_STRING)) != 0)
3547                     {
3548                         _current.typeId = readTypeId((_current.sliceFlags & Protocol.FLAG_HAS_TYPE_ID_INDEX) != 0);
3549                         _current.compactId = -1;
3550                     }
3551                     else
3552                     {
3553                         // Only the most derived slice encodes the type ID for the compact format.
3554                         _current.typeId = "";
3555                         _current.compactId = -1;
3556                     }
3557                 }
3558                 else
3559                 {
3560                     _current.typeId = _stream.readString();
3561                     _current.compactId = -1;
3562                 }
3563 
3564                 //
3565                 // Read the slice size if necessary.
3566                 //
3567                 if((_current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
3568                 {
3569                     _current.sliceSize = _stream.readInt();
3570                     if(_current.sliceSize < 4)
3571                     {
3572                         throw new UnmarshalOutOfBoundsException();
3573                     }
3574                 }
3575                 else
3576                 {
3577                     _current.sliceSize = 0;
3578                 }
3579 
3580                 return _current.typeId;
3581             }
3582 
endSlice()3583             internal override void endSlice()
3584             {
3585                 if((_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0)
3586                 {
3587                     _stream.skipOptionals();
3588                 }
3589 
3590                 //
3591                 // Read the indirection table if one is present and transform the
3592                 // indirect patch list into patch entries with direct references.
3593                 //
3594                 if((_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
3595                 {
3596                     //
3597                     // The table is written as a sequence<size> to conserve space.
3598                     //
3599                     int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)];
3600                     for(int i = 0; i < indirectionTable.Length; ++i)
3601                     {
3602                         indirectionTable[i] = readInstance(_stream.readSize(), null);
3603                     }
3604 
3605                     //
3606                     // Sanity checks. If there are optional members, it's possible
3607                     // that not all instance references were read if they are from
3608                     // unknown optional data members.
3609                     //
3610                     if(indirectionTable.Length == 0)
3611                     {
3612                         throw new MarshalException("empty indirection table");
3613                     }
3614                     if((_current.indirectPatchList == null || _current.indirectPatchList.Count == 0) &&
3615                        (_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) == 0)
3616                     {
3617                         throw new MarshalException("no references to indirection table");
3618                     }
3619 
3620                     //
3621                     // Convert indirect references into direct references.
3622                     //
3623                     if(_current.indirectPatchList != null)
3624                     {
3625                         foreach(IndirectPatchEntry e in _current.indirectPatchList)
3626                         {
3627                             Debug.Assert(e.index >= 0);
3628                             if(e.index >= indirectionTable.Length)
3629                             {
3630                                 throw new MarshalException("indirection out of range");
3631                             }
3632                             addPatchEntry(indirectionTable[e.index], e.patcher);
3633                         }
3634                         _current.indirectPatchList.Clear();
3635                     }
3636                 }
3637             }
3638 
skipSlice()3639             internal override void skipSlice()
3640             {
3641                 if(_stream.instance().traceLevels().slicing > 0)
3642                 {
3643                     Logger logger = _stream.instance().initializationData().logger;
3644                     string slicingCat = _stream.instance().traceLevels().slicingCat;
3645                     if(_current.sliceType == SliceType.ExceptionSlice)
3646                     {
3647                         IceInternal.TraceUtil.traceSlicing("exception", _current.typeId, slicingCat, logger);
3648                     }
3649                     else
3650                     {
3651                         IceInternal.TraceUtil.traceSlicing("object", _current.typeId, slicingCat, logger);
3652                     }
3653                 }
3654 
3655                 int start = _stream.pos();
3656 
3657                 if((_current.sliceFlags & Protocol.FLAG_HAS_SLICE_SIZE) != 0)
3658                 {
3659                     Debug.Assert(_current.sliceSize >= 4);
3660                     _stream.skip(_current.sliceSize - 4);
3661                 }
3662                 else
3663                 {
3664                     if(_current.sliceType == SliceType.ValueSlice)
3665                     {
3666                         throw new NoValueFactoryException("no value factory found and compact format prevents " +
3667                                                           "slicing (the sender should use the sliced format " +
3668                                                           "instead)", _current.typeId);
3669                     }
3670                     else
3671                     {
3672                         if(_current.typeId.StartsWith("::", StringComparison.Ordinal))
3673                         {
3674                             throw new UnknownUserException(_current.typeId.Substring(2));
3675                         }
3676                         else
3677                         {
3678                             throw new UnknownUserException(_current.typeId);
3679                         }
3680                     }
3681                 }
3682 
3683                 //
3684                 // Preserve this slice.
3685                 //
3686                 SliceInfo info = new SliceInfo();
3687                 info.typeId = _current.typeId;
3688                 info.compactId = _current.compactId;
3689                 info.hasOptionalMembers = (_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0;
3690                 info.isLastSlice = (_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0;
3691                 IceInternal.ByteBuffer b = _stream.getBuffer().b;
3692                 int end = b.position();
3693                 int dataEnd = end;
3694                 if(info.hasOptionalMembers)
3695                 {
3696                     //
3697                     // Don't include the optional member end marker. It will be re-written by
3698                     // endSlice when the sliced data is re-written.
3699                     //
3700                     --dataEnd;
3701                 }
3702                 info.bytes = new byte[dataEnd - start];
3703                 b.position(start);
3704                 b.get(info.bytes);
3705                 b.position(end);
3706 
3707                 if(_current.slices == null)
3708                 {
3709                     _current.slices = new List<SliceInfo>();
3710                     _current.indirectionTables = new List<int[]>();
3711                 }
3712 
3713                 //
3714                 // Read the indirect instance table. We read the instances or their
3715                 // IDs if the instance is a reference to an already unmarshaled
3716                 // instance.
3717                 //
3718                 if((_current.sliceFlags & Protocol.FLAG_HAS_INDIRECTION_TABLE) != 0)
3719                 {
3720                     int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)];
3721                     for(int i = 0; i < indirectionTable.Length; ++i)
3722                     {
3723                         indirectionTable[i] = readInstance(_stream.readSize(), null);
3724                     }
3725                     _current.indirectionTables.Add(indirectionTable);
3726                 }
3727                 else
3728                 {
3729                     _current.indirectionTables.Add(null);
3730                 }
3731 
3732                 _current.slices.Add(info);
3733             }
3734 
readOptional(int readTag, OptionalFormat expectedFormat)3735             internal override bool readOptional(int readTag, OptionalFormat expectedFormat)
3736             {
3737                 if(_current == null)
3738                 {
3739                     return _stream.readOptImpl(readTag, expectedFormat);
3740                 }
3741                 else if((_current.sliceFlags & Protocol.FLAG_HAS_OPTIONAL_MEMBERS) != 0)
3742                 {
3743                     return _stream.readOptImpl(readTag, expectedFormat);
3744                 }
3745                 return false;
3746             }
3747 
readInstance(int index, System.Action<Value> cb)3748             private int readInstance(int index, System.Action<Value> cb)
3749             {
3750                 Debug.Assert(index > 0);
3751 
3752                 if(index > 1)
3753                 {
3754                     if(cb != null)
3755                     {
3756                         addPatchEntry(index, cb);
3757                     }
3758                     return index;
3759                 }
3760 
3761                 push(SliceType.ValueSlice);
3762 
3763                 //
3764                 // Get the instance ID before we start reading slices. If some
3765                 // slices are skipped, the indirect instance table are still read and
3766                 // might read other instances.
3767                 //
3768                 index = ++_valueIdIndex;
3769 
3770                 //
3771                 // Read the first slice header.
3772                 //
3773                 startSlice();
3774                 string mostDerivedId = _current.typeId;
3775                 Value v = null;
3776                 while(true)
3777                 {
3778                     bool updateCache = false;
3779 
3780                     if(_current.compactId >= 0)
3781                     {
3782                         updateCache = true;
3783 
3784                         //
3785                         // Translate a compact (numeric) type ID into a class.
3786                         //
3787                         if(_compactIdCache == null)
3788                         {
3789                             _compactIdCache = new Dictionary<int, Type>(); // Lazy initialization.
3790                         }
3791                         else
3792                         {
3793                             //
3794                             // Check the cache to see if we've already translated the compact type ID into a class.
3795                             //
3796                             Type cls = null;
3797                             _compactIdCache.TryGetValue(_current.compactId, out cls);
3798                             if(cls != null)
3799                             {
3800                                 try
3801                                 {
3802                                     Debug.Assert(!cls.IsAbstract && !cls.IsInterface);
3803                                     v = (Value)IceInternal.AssemblyUtil.createInstance(cls);
3804                                     updateCache = false;
3805                                 }
3806                                 catch(Exception ex)
3807                                 {
3808                                     throw new NoValueFactoryException("no value factory", "compact ID " +
3809                                                                       _current.compactId, ex);
3810                                 }
3811                             }
3812                         }
3813 
3814                         //
3815                         // If we haven't already cached a class for the compact ID, then try to translate the
3816                         // compact ID into a type ID.
3817                         //
3818                         if(v == null)
3819                         {
3820                             _current.typeId = "";
3821                             if(_compactIdResolver != null)
3822                             {
3823                                 try
3824                                 {
3825                                     _current.typeId = _compactIdResolver(_current.compactId);
3826                                 }
3827                                 catch(LocalException)
3828                                 {
3829                                     throw;
3830                                 }
3831                                 catch(System.Exception ex)
3832                                 {
3833                                     throw new MarshalException("exception in CompactIdResolver for ID " +
3834                                                                    _current.compactId, ex);
3835                                 }
3836                             }
3837 
3838                             if(_current.typeId.Length == 0)
3839                             {
3840                                 _current.typeId = _stream.instance().resolveCompactId(_current.compactId);
3841                             }
3842                         }
3843                     }
3844 
3845                     if(v == null && _current.typeId.Length > 0)
3846                     {
3847                         v = newInstance(_current.typeId);
3848                     }
3849 
3850                     if(v != null)
3851                     {
3852                         if(updateCache)
3853                         {
3854                             Debug.Assert(_current.compactId >= 0);
3855                             _compactIdCache.Add(_current.compactId, v.GetType());
3856                         }
3857 
3858                         //
3859                         // We have an instance, get out of this loop.
3860                         //
3861                         break;
3862                     }
3863 
3864                     //
3865                     // If slicing is disabled, stop unmarshaling.
3866                     //
3867                     if(!_sliceValues)
3868                     {
3869                         throw new NoValueFactoryException("no value factory found and slicing is disabled",
3870                                                           _current.typeId);
3871                     }
3872 
3873                     //
3874                     // Slice off what we don't understand.
3875                     //
3876                     skipSlice();
3877 
3878                     //
3879                     // If this is the last slice, keep the instance as an opaque
3880                     // UnknownSlicedValue object.
3881                     //
3882                     if((_current.sliceFlags & Protocol.FLAG_IS_LAST_SLICE) != 0)
3883                     {
3884                         //
3885                         // Provide a factory with an opportunity to supply the instance.
3886                         // We pass the "::Ice::Object" ID to indicate that this is the
3887                         // last chance to preserve the instance.
3888                         //
3889                         v = newInstance(Value.ice_staticId());
3890                         if(v == null)
3891                         {
3892                             v = new UnknownSlicedValue(mostDerivedId);
3893                         }
3894 
3895                         break;
3896                     }
3897 
3898                     startSlice(); // Read next Slice header for next iteration.
3899                 }
3900 
3901                 if(++_classGraphDepth > _classGraphDepthMax)
3902                 {
3903                     throw new MarshalException("maximum class graph depth reached");
3904                 }
3905 
3906                 //
3907                 // Unmarshal the instance.
3908                 //
3909                 unmarshal(index, v);
3910 
3911                 --_classGraphDepth;
3912 
3913                 if(_current == null && _patchMap != null && _patchMap.Count > 0)
3914                 {
3915                     //
3916                     // If any entries remain in the patch map, the sender has sent an index for an instance, but failed
3917                     // to supply the instance.
3918                     //
3919                     throw new MarshalException("index for class received, but no instance");
3920                 }
3921 
3922                 if(cb != null)
3923                 {
3924                     cb(v);
3925                 }
3926                 return index;
3927             }
3928 
readSlicedData()3929             private SlicedData readSlicedData()
3930             {
3931                 if(_current.slices == null) // No preserved slices.
3932                 {
3933                     return null;
3934                 }
3935 
3936                 //
3937                 // The _indirectionTables member holds the indirection table for each slice
3938                 // in _slices.
3939                 //
3940                 Debug.Assert(_current.slices.Count == _current.indirectionTables.Count);
3941                 for(int n = 0; n < _current.slices.Count; ++n)
3942                 {
3943                     //
3944                     // We use the "instances" list in SliceInfo to hold references
3945                     // to the target instances. Note that the instances might not have
3946                     // been read yet in the case of a circular reference to an
3947                     // enclosing instance.
3948                     //
3949                     int[] table = _current.indirectionTables[n];
3950                     SliceInfo info = _current.slices[n];
3951                     info.instances = new Value[table != null ? table.Length : 0];
3952                     for(int j = 0; j < info.instances.Length; ++j)
3953                     {
3954                         var cj = j;
3955                         addPatchEntry(table[j], (Ice.Value v) => info.instances[cj] = v);
3956                     }
3957                 }
3958 
3959                 return new SlicedData(_current.slices.ToArray());
3960             }
3961 
push(SliceType sliceType)3962             private void push(SliceType sliceType)
3963             {
3964                 if(_current == null)
3965                 {
3966                     _current = new InstanceData(null);
3967                 }
3968                 else
3969                 {
3970                     _current = _current.next == null ? new InstanceData(_current) : _current.next;
3971                 }
3972                 _current.sliceType = sliceType;
3973                 _current.skipFirstSlice = false;
3974             }
3975 
3976             private sealed class IndirectPatchEntry
3977             {
3978                 public int index;
3979                 public System.Action<Value> patcher;
3980             }
3981 
3982             private sealed class InstanceData
3983             {
InstanceData(InstanceData previous)3984                 internal InstanceData(InstanceData previous)
3985                 {
3986                     if(previous != null)
3987                     {
3988                         previous.next = this;
3989                     }
3990                     this.previous = previous;
3991                     this.next = null;
3992                 }
3993 
3994                 // Instance attributes
3995                 internal SliceType sliceType;
3996                 internal bool skipFirstSlice;
3997                 internal List<SliceInfo> slices;     // Preserved slices.
3998                 internal List<int[]> indirectionTables;
3999 
4000                 // Slice attributes
4001                 internal byte sliceFlags;
4002                 internal int sliceSize;
4003                 internal string typeId;
4004                 internal int compactId;
4005                 internal Stack<IndirectPatchEntry> indirectPatchList;
4006 
4007                 internal InstanceData previous;
4008                 internal InstanceData next;
4009             }
4010 
4011             private System.Func<int, string> _compactIdResolver;
4012             private InstanceData _current;
4013             private int _valueIdIndex; // The ID of the next instance to unmarshal.
4014             private Dictionary<int, Type> _compactIdCache;
4015         }
4016 
4017         private sealed class Encaps
4018         {
reset()4019             internal void reset()
4020             {
4021                 decoder = null;
4022             }
4023 
setEncoding(EncodingVersion encoding)4024             internal void setEncoding(EncodingVersion encoding)
4025             {
4026                 this.encoding = encoding;
4027                 encoding_1_0 = encoding.Equals(Util.Encoding_1_0);
4028             }
4029 
4030             internal int start;
4031             internal int sz;
4032             internal EncodingVersion encoding;
4033             internal bool encoding_1_0;
4034 
4035             internal EncapsDecoder decoder;
4036 
4037             internal Encaps next;
4038         }
4039 
4040         //
4041         // The encoding version to use when there's no encapsulation to
4042         // read from. This is for example used to read message headers.
4043         //
4044         private EncodingVersion _encoding;
4045 
isEncoding_1_0()4046         private bool isEncoding_1_0()
4047         {
4048             return _encapsStack != null ? _encapsStack.encoding_1_0 : _encoding.Equals(Util.Encoding_1_0);
4049         }
4050 
4051         private Encaps _encapsStack;
4052         private Encaps _encapsCache;
4053 
initEncaps()4054         private void initEncaps()
4055         {
4056             if(_encapsStack == null) // Lazy initialization
4057             {
4058                 _encapsStack = _encapsCache;
4059                 if(_encapsStack != null)
4060                 {
4061                     _encapsCache = _encapsCache.next;
4062                 }
4063                 else
4064                 {
4065                     _encapsStack = new Encaps();
4066                 }
4067                 _encapsStack.setEncoding(_encoding);
4068                 _encapsStack.sz = _buf.b.limit();
4069             }
4070 
4071             if(_encapsStack.decoder == null) // Lazy initialization.
4072             {
4073                 if(_encapsStack.encoding_1_0)
4074                 {
4075                     _encapsStack.decoder = new EncapsDecoder10(this, _encapsStack, _sliceValues, _classGraphDepthMax,
4076                                                                _valueFactoryManager, _classResolver);
4077                 }
4078                 else
4079                 {
4080                     _encapsStack.decoder = new EncapsDecoder11(this, _encapsStack, _sliceValues, _classGraphDepthMax,
4081                                                                _valueFactoryManager, _classResolver, _compactIdResolver);
4082                 }
4083             }
4084         }
4085 
4086         private bool _sliceValues;
4087         private bool _traceSlicing;
4088         private int _classGraphDepthMax;
4089 
4090         private int _startSeq;
4091         private int _minSeqSize;
4092 
4093         private ValueFactoryManager _valueFactoryManager;
4094         private Logger _logger;
4095         private System.Func<int, string> _compactIdResolver;
4096         private System.Func<string, Type> _classResolver;
4097     }
4098 
4099     /// <summary>
4100     /// Base class for extracting class instances from an input stream.
4101     /// </summary>
4102     public abstract class ValueReader : Value
4103     {
4104         /// <summary>
4105         /// Read the instance's data members.
4106         /// </summary>
4107         /// <param name="inStream">The input stream to read from.</param>
read(InputStream inStream)4108         public abstract void read(InputStream inStream);
4109 
iceWrite(OutputStream os)4110         public override void iceWrite(OutputStream os)
4111         {
4112             Debug.Assert(false);
4113         }
4114 
iceRead(InputStream istr)4115         public override void iceRead(InputStream istr)
4116         {
4117             read(istr);
4118         }
4119     }
4120 
4121 }
4122