Fix a bug when setting capacity to 0 on MemoryStream causes crash on ToArray call
[mono-project.git] / mcs / class / referencesource / mscorlib / system / io / memorystream.cs
blobd5f9b48606b6d4fa0f4049cb831429a9d55f8859
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7 **
8 ** Class: MemoryStream
9 **
10 ** <OWNER>[....]</OWNER>
13 ** Purpose: A Stream whose backing store is memory. Great
14 ** for temporary storage without creating a temp file. Also
15 ** lets users expose a byte[] as a stream.
18 ===========================================================*/
20 using System;
21 using System.Runtime;
22 using System.Runtime.CompilerServices;
23 using System.Runtime.InteropServices;
24 using System.Diagnostics.Contracts;
25 #if FEATURE_ASYNC_IO
26 using System.Threading;
27 using System.Threading.Tasks;
28 using System.Security.Permissions;
29 #endif
31 namespace System.IO {
32 // A MemoryStream represents a Stream in memory (ie, it has no backing store).
33 // This stream may reduce the need for temporary buffers and files in
34 // an application.
35 //
36 // There are two ways to create a MemoryStream. You can initialize one
37 // from an unsigned byte array, or you can create an empty one. Empty
38 // memory streams are resizable, while ones created with a byte array provide
39 // a stream "view" of the data.
40 [Serializable]
41 [ComVisible(true)]
42 public class MemoryStream : Stream
44 private byte[] _buffer; // Either allocated internally or externally.
45 private int _origin; // For user-provided arrays, start at this origin
46 private int _position; // read/write head.
47 [ContractPublicPropertyName("Length")]
48 private int _length; // Number of bytes within the memory stream
49 private int _capacity; // length of usable portion of buffer for stream
50 // Note that _capacity == _buffer.Length for non-user-provided byte[]'s
52 private bool _expandable; // User-provided buffers aren't expandable.
53 private bool _writable; // Can user write to this stream?
54 private bool _exposable; // Whether the array can be returned to the user.
55 private bool _isOpen; // Is this stream open or closed?
57 #if FEATURE_ASYNC_IO
58 [NonSerialized]
59 private Task<int> _lastReadTask; // The last successful task returned from ReadAsync
60 #endif
62 // <
64 private const int MemStreamMaxLength = Int32.MaxValue;
66 public MemoryStream()
67 : this(0) {
70 public MemoryStream(int capacity) {
71 if (capacity < 0) {
72 throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"));
74 Contract.EndContractBlock();
76 _buffer = new byte[capacity];
77 _capacity = capacity;
78 _expandable = true;
79 _writable = true;
80 _exposable = true;
81 _origin = 0; // Must be 0 for byte[]'s created by MemoryStream
82 _isOpen = true;
85 #if !FEATURE_CORECLR
86 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
87 #endif
88 public MemoryStream(byte[] buffer)
89 : this(buffer, true) {
92 public MemoryStream(byte[] buffer, bool writable) {
93 if (buffer == null) throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
94 Contract.EndContractBlock();
95 _buffer = buffer;
96 _length = _capacity = buffer.Length;
97 _writable = writable;
98 _exposable = false;
99 _origin = 0;
100 _isOpen = true;
103 public MemoryStream(byte[] buffer, int index, int count)
104 : this(buffer, index, count, true, false) {
107 public MemoryStream(byte[] buffer, int index, int count, bool writable)
108 : this(buffer, index, count, writable, false) {
111 public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) {
112 if (buffer==null)
113 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
114 if (index < 0)
115 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
116 if (count < 0)
117 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
118 if (buffer.Length - index < count)
119 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
120 Contract.EndContractBlock();
122 _buffer = buffer;
123 _origin = _position = index;
124 _length = _capacity = index + count;
125 _writable = writable;
126 _exposable = publiclyVisible; // Can GetBuffer return the array?
127 _expandable = false;
128 _isOpen = true;
131 public override bool CanRead {
132 [Pure]
133 get { return _isOpen; }
136 public override bool CanSeek {
137 [Pure]
138 get { return _isOpen; }
141 public override bool CanWrite {
142 [Pure]
143 get { return _writable; }
146 private void EnsureWriteable() {
147 // Previously, instead of calling CanWrite we just checked the _writable field directly, and some code
148 // took a dependency on that behavior.
149 #if FEATURE_CORECLR
150 if (IsAppEarlierThanSl4) {
151 if (!_writable) __Error.WriteNotSupported();
152 } else {
153 if (!CanWrite) __Error.WriteNotSupported();
155 #else
156 if (!CanWrite) __Error.WriteNotSupported();
157 #endif
160 protected override void Dispose(bool disposing)
162 try {
163 if (disposing) {
164 _isOpen = false;
165 _writable = false;
166 _expandable = false;
167 // Don't set buffer to null - allow GetBuffer & ToArray to work.
168 #if FEATURE_ASYNC_IO
169 _lastReadTask = null;
170 #endif
173 finally {
174 // Call base.Close() to cleanup async IO resources
175 base.Dispose(disposing);
179 // returns a bool saying whether we allocated a new array.
180 private bool EnsureCapacity(int value) {
181 // Check for overflow
182 if (value < 0)
183 throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
184 if (value > _capacity) {
185 int newCapacity = value;
186 if (newCapacity < 256)
187 newCapacity = 256;
188 if (newCapacity < _capacity * 2)
189 newCapacity = _capacity * 2;
190 Capacity = newCapacity;
191 return true;
193 return false;
196 public override void Flush() {
199 #if FEATURE_ASYNC_IO
200 [HostProtection(ExternalThreading=true)]
201 [ComVisible(false)]
202 public override Task FlushAsync(CancellationToken cancellationToken) {
204 if (cancellationToken.IsCancellationRequested)
205 return Task.FromCancellation(cancellationToken);
207 try {
209 Flush();
210 return Task.CompletedTask;
212 } catch(Exception ex) {
214 return Task.FromException(ex);
217 #endif // FEATURE_ASYNC_IO
220 #if !FEATURE_CORECLR
221 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
222 #endif
223 public virtual byte[] GetBuffer() {
224 if (!_exposable)
225 throw new UnauthorizedAccessException(Environment.GetResourceString("UnauthorizedAccess_MemStreamBuffer"));
226 return _buffer;
229 // -------------- PERF: Internal functions for fast direct access of MemoryStream buffer (cf. BinaryReader for usage) ---------------
231 // PERF: Internal sibling of GetBuffer, always returns a buffer (cf. GetBuffer())
232 #if !FEATURE_CORECLR
233 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
234 #endif
235 internal byte[] InternalGetBuffer() {
236 return _buffer;
239 // PERF: Get origin and length - used in ResourceWriter.
240 #if !FEATURE_CORECLR
241 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
242 #endif
243 [FriendAccessAllowed]
244 internal void InternalGetOriginAndLength(out int origin, out int length)
246 if (!_isOpen) __Error.StreamIsClosed();
247 origin = _origin;
248 length = _length;
251 // PERF: True cursor position, we don't need _origin for direct access
252 #if !FEATURE_CORECLR
253 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
254 #endif
255 internal int InternalGetPosition() {
256 if (!_isOpen) __Error.StreamIsClosed();
257 return _position;
260 // PERF: Takes out Int32 as fast as possible
261 internal int InternalReadInt32() {
262 if (!_isOpen)
263 __Error.StreamIsClosed();
265 int pos = (_position += 4); // use temp to avoid ----
266 if (pos > _length)
268 _position = _length;
269 __Error.EndOfFile();
271 return (int)(_buffer[pos-4] | _buffer[pos-3] << 8 | _buffer[pos-2] << 16 | _buffer[pos-1] << 24);
274 // PERF: Get actual length of bytes available for read; do sanity checks; shift position - i.e. everything except actual copying bytes
275 internal int InternalEmulateRead(int count) {
276 if (!_isOpen) __Error.StreamIsClosed();
278 int n = _length - _position;
279 if (n > count) n = count;
280 if (n < 0) n = 0;
282 Contract.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1.
283 _position += n;
284 return n;
287 // Gets & sets the capacity (number of bytes allocated) for this stream.
288 // The capacity cannot be set to a value less than the current length
289 // of the stream.
291 public virtual int Capacity {
292 get {
293 if (!_isOpen) __Error.StreamIsClosed();
294 return _capacity - _origin;
296 set {
297 // Only update the capacity if the MS is expandable and the value is different than the current capacity.
298 // Special behavior if the MS isn't expandable: we don't throw if value is the same as the current capacity
299 #if !FEATURE_CORECLR
300 if (value < Length) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
301 #endif
302 Contract.Ensures(_capacity - _origin == value);
303 Contract.EndContractBlock();
305 #if FEATURE_CORECLR
306 if (IsAppEarlierThanSl4) {
307 if (value < _length) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
308 } else {
309 if (value < Length) throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
311 #endif
313 if (!_isOpen) __Error.StreamIsClosed();
314 if (!_expandable && (value != Capacity)) __Error.MemoryStreamNotExpandable();
316 // MemoryStream has this invariant: _origin > 0 => !expandable (see ctors)
317 if (_expandable && value != _capacity) {
318 if (value > 0) {
319 byte[] newBuffer = new byte[value];
320 if (_length > 0) Buffer.InternalBlockCopy(_buffer, 0, newBuffer, 0, _length);
321 _buffer = newBuffer;
323 else {
324 _buffer = null;
326 _capacity = value;
331 public override long Length {
332 #if !FEATURE_CORECLR
333 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
334 #endif
335 get {
336 if (!_isOpen) __Error.StreamIsClosed();
337 return _length - _origin;
341 public override long Position {
342 #if !FEATURE_CORECLR
343 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
344 #endif
345 get {
346 if (!_isOpen) __Error.StreamIsClosed();
347 return _position - _origin;
349 set {
350 if (value < 0)
351 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
352 Contract.Ensures(Position == value);
353 Contract.EndContractBlock();
355 if (!_isOpen) __Error.StreamIsClosed();
357 if (value > MemStreamMaxLength)
358 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
359 _position = _origin + (int)value;
363 #if FEATURE_CORECLR
364 private static bool IsAppEarlierThanSl4 {
365 get {
366 return CompatibilitySwitches.IsAppEarlierThanSilverlight4;
369 #endif
371 public override int Read([In, Out] byte[] buffer, int offset, int count) {
372 if (buffer==null)
373 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
374 if (offset < 0)
375 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
376 if (count < 0)
377 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
378 if (buffer.Length - offset < count)
379 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
380 Contract.EndContractBlock();
382 if (!_isOpen) __Error.StreamIsClosed();
384 int n = _length - _position;
385 if (n > count) n = count;
386 if (n <= 0)
387 return 0;
389 Contract.Assert(_position + n >= 0, "_position + n >= 0"); // len is less than 2^31 -1.
391 if (n <= 8)
393 int byteCount = n;
394 while (--byteCount >= 0)
395 buffer[offset + byteCount] = _buffer[_position + byteCount];
397 else
398 Buffer.InternalBlockCopy(_buffer, _position, buffer, offset, n);
399 _position += n;
401 return n;
404 #if FEATURE_ASYNC_IO
405 [HostProtection(ExternalThreading = true)]
406 [ComVisible(false)]
407 public override Task<int> ReadAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
409 if (buffer==null)
410 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
411 if (offset < 0)
412 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
413 if (count < 0)
414 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
415 if (buffer.Length - offset < count)
416 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
417 Contract.EndContractBlock(); // contract validation copied from Read(...)
419 // If cancellation was requested, bail early
420 if (cancellationToken.IsCancellationRequested)
421 return Task.FromCancellation<int>(cancellationToken);
425 int n = Read(buffer, offset, count);
426 var t = _lastReadTask;
427 Contract.Assert(t == null || t.Status == TaskStatus.RanToCompletion,
428 "Expected that a stored last task completed successfully");
429 return (t != null && t.Result == n) ? t : (_lastReadTask = Task.FromResult<int>(n));
431 catch (OperationCanceledException oce)
433 return Task.FromCancellation<int>(oce);
435 catch (Exception exception)
437 return Task.FromException<int>(exception);
440 #endif //FEATURE_ASYNC_IO
443 public override int ReadByte() {
444 if (!_isOpen) __Error.StreamIsClosed();
446 if (_position >= _length) return -1;
448 return _buffer[_position++];
452 #if FEATURE_ASYNC_IO
453 public override Task CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken) {
455 // This implementation offers beter performance compared to the base class version.
457 // The parameter checks must be in [....] with the base version:
458 if (destination == null)
459 throw new ArgumentNullException("destination");
461 if (bufferSize <= 0)
462 throw new ArgumentOutOfRangeException("bufferSize", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
464 if (!CanRead && !CanWrite)
465 throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_StreamClosed"));
467 if (!destination.CanRead && !destination.CanWrite)
468 throw new ObjectDisposedException("destination", Environment.GetResourceString("ObjectDisposed_StreamClosed"));
470 if (!CanRead)
471 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream"));
473 if (!destination.CanWrite)
474 throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnwritableStream"));
476 Contract.EndContractBlock();
478 // If we have been inherited into a subclass, the following implementation could be incorrect
479 // since it does not call through to Read() or Write() which a subclass might have overriden.
480 // To be safe we will only use this implementation in cases where we know it is safe to do so,
481 // and delegate to our base class (which will call into Read/Write) when we are not sure.
482 if (this.GetType() != typeof(MemoryStream))
483 return base.CopyToAsync(destination, bufferSize, cancellationToken);
485 // If cancelled - return fast:
486 if (cancellationToken.IsCancellationRequested)
487 return Task.FromCancellation(cancellationToken);
489 // Avoid copying data from this buffer into a temp buffer:
490 // (require that InternalEmulateRead does not throw,
491 // otherwise it needs to be wrapped into try-catch-Task.FromException like memStrDest.Write below)
493 Int32 pos = _position;
494 Int32 n = InternalEmulateRead(_length - _position);
496 // If destination is not a memory stream, write there asynchronously:
497 MemoryStream memStrDest = destination as MemoryStream;
498 if (memStrDest == null)
499 return destination.WriteAsync(_buffer, pos, n, cancellationToken);
501 try {
503 // If destination is a MemoryStream, CopyTo synchronously:
504 memStrDest.Write(_buffer, pos, n);
505 return Task.CompletedTask;
507 } catch(Exception ex) {
508 return Task.FromException(ex);
511 #endif //FEATURE_ASYNC_IO
514 public override long Seek(long offset, SeekOrigin loc) {
515 if (!_isOpen) __Error.StreamIsClosed();
517 if (offset > MemStreamMaxLength)
518 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
519 switch(loc) {
520 case SeekOrigin.Begin: {
521 int tempPosition = unchecked(_origin + (int)offset);
522 if (offset < 0 || tempPosition < _origin)
523 throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
524 _position = tempPosition;
525 break;
527 case SeekOrigin.Current: {
528 int tempPosition = unchecked(_position + (int)offset);
529 if (unchecked(_position + offset) < _origin || tempPosition < _origin)
530 throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
531 _position = tempPosition;
532 break;
534 case SeekOrigin.End: {
535 int tempPosition = unchecked(_length + (int)offset);
536 if ( unchecked(_length + offset) < _origin || tempPosition < _origin )
537 throw new IOException(Environment.GetResourceString("IO.IO_SeekBeforeBegin"));
538 _position = tempPosition;
539 break;
541 default:
542 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidSeekOrigin"));
545 Contract.Assert(_position >= 0, "_position >= 0");
546 return _position;
549 // Sets the length of the stream to a given value. The new
550 // value must be nonnegative and less than the space remaining in
551 // the array, Int32.MaxValue - origin
552 // Origin is 0 in all cases other than a MemoryStream created on
553 // top of an existing array and a specific starting offset was passed
554 // into the MemoryStream constructor. The upper bounds prevents any
555 // situations where a stream may be created on top of an array then
556 // the stream is made longer than the maximum possible length of the
557 // array (Int32.MaxValue).
559 public override void SetLength(long value) {
560 if (value < 0 || value > Int32.MaxValue) {
561 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
563 Contract.Ensures(_length - _origin == value);
564 Contract.EndContractBlock();
565 EnsureWriteable();
567 // Origin wasn't publicly exposed above.
568 Contract.Assert(MemStreamMaxLength == Int32.MaxValue); // Check parameter validation logic in this method if this fails.
569 if (value > (Int32.MaxValue - _origin)) {
570 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_StreamLength"));
573 int newLength = _origin + (int)value;
574 bool allocatedNewArray = EnsureCapacity(newLength);
575 if (!allocatedNewArray && newLength > _length)
576 Array.Clear(_buffer, _length, newLength - _length);
577 _length = newLength;
578 if (_position > newLength) _position = newLength;
582 public virtual byte[] ToArray() {
583 #if MONO
584 // Bug fix for BCL bug when _buffer is null
585 if (_length - _origin == 0)
586 return EmptyArray<byte>.Value;
587 #endif
588 BCLDebug.Perf(_exposable, "MemoryStream::GetBuffer will let you avoid a copy.");
589 byte[] copy = new byte[_length - _origin];
590 Buffer.InternalBlockCopy(_buffer, _origin, copy, 0, _length - _origin);
591 return copy;
594 public override void Write(byte[] buffer, int offset, int count) {
595 if (buffer==null)
596 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
597 if (offset < 0)
598 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
599 if (count < 0)
600 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
601 if (buffer.Length - offset < count)
602 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
603 Contract.EndContractBlock();
605 if (!_isOpen) __Error.StreamIsClosed();
606 EnsureWriteable();
608 int i = _position + count;
609 // Check for overflow
610 if (i < 0)
611 throw new IOException(Environment.GetResourceString("IO.IO_StreamTooLong"));
613 if (i > _length) {
614 bool mustZero = _position > _length;
615 if (i > _capacity) {
616 bool allocatedNewArray = EnsureCapacity(i);
617 if (allocatedNewArray)
618 mustZero = false;
620 if (mustZero)
621 Array.Clear(_buffer, _length, i - _length);
622 _length = i;
624 if ((count <= 8) && (buffer != _buffer))
626 int byteCount = count;
627 while (--byteCount >= 0)
628 _buffer[_position + byteCount] = buffer[offset + byteCount];
630 else
631 Buffer.InternalBlockCopy(buffer, offset, _buffer, _position, count);
632 _position = i;
636 #if FEATURE_ASYNC_IO
637 [HostProtection(ExternalThreading = true)]
638 [ComVisible(false)]
639 public override Task WriteAsync(Byte[] buffer, int offset, int count, CancellationToken cancellationToken)
641 if (buffer == null)
642 throw new ArgumentNullException("buffer", Environment.GetResourceString("ArgumentNull_Buffer"));
643 if (offset < 0)
644 throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
645 if (count < 0)
646 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
647 if (buffer.Length - offset < count)
648 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
649 Contract.EndContractBlock(); // contract validation copied from Write(...)
651 // If cancellation is already requested, bail early
652 if (cancellationToken.IsCancellationRequested)
653 return Task.FromCancellation(cancellationToken);
657 Write(buffer, offset, count);
658 return Task.CompletedTask;
660 catch (OperationCanceledException oce)
662 return Task.FromCancellation<VoidTaskResult>(oce);
664 catch (Exception exception)
666 return Task.FromException(exception);
669 #endif // FEATURE_ASYNC_IO
671 public override void WriteByte(byte value) {
672 if (!_isOpen) __Error.StreamIsClosed();
673 EnsureWriteable();
675 if (_position >= _length) {
676 int newLength = _position + 1;
677 bool mustZero = _position > _length;
678 if (newLength >= _capacity) {
679 bool allocatedNewArray = EnsureCapacity(newLength);
680 if (allocatedNewArray)
681 mustZero = false;
683 if (mustZero)
684 Array.Clear(_buffer, _length, _position - _length);
685 _length = newLength;
687 _buffer[_position++] = value;
691 // Writes this MemoryStream to another stream.
692 public virtual void WriteTo(Stream stream) {
693 if (stream==null)
694 throw new ArgumentNullException("stream", Environment.GetResourceString("ArgumentNull_Stream"));
695 Contract.EndContractBlock();
697 if (!_isOpen) __Error.StreamIsClosed();
698 stream.Write(_buffer, _origin, _length - _origin);
701 #if CONTRACTS_FULL
702 [ContractInvariantMethod]
703 private void ObjectInvariantMS() {
704 Contract.Invariant(_origin >= 0);
705 Contract.Invariant(_origin <= _position);
706 Contract.Invariant(_length <= _capacity);
707 // equivalent to _origin > 0 => !expandable, and using fact that _origin is non-negative.
708 Contract.Invariant(_origin == 0 || !_expandable);
710 #endif