Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 5dfdcf6

Browse files
committed
Update RecyclableMemoryStream to latest version
1 parent 9f8436e commit 5dfdcf6

File tree

1 file changed

+46
-48
lines changed

1 file changed

+46
-48
lines changed

src/ServiceStack.Text/RecyclableMemoryStream.cs

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ namespace ServiceStack.Text //Internalize to avoid conflicts
3737
public static class MemoryStreamFactory
3838
{
3939
public static bool UseRecyclableMemoryStream { get; set; }
40-
public static bool MuteDuplicateDisposeExceptions { get; set; }
4140

4241
#if !SL5
4342
public static RecyclableMemoryStreamManager RecyclableInstance = new RecyclableMemoryStreamManager();
@@ -722,7 +721,7 @@ public MemoryStream GetStream(string tag, byte[] buffer, int offset, int count)
722721
/// leads to continual memory growth as each stream approaches the maximum allowed size.
723722
/// 3. Memory copying - Each time a MemoryStream grows, all the bytes are copied into new buffers.
724723
/// This implementation only copies the bytes when GetBuffer is called.
725-
/// 4. Memory fragmentation - By using homogenous buffer sizes, it ensures that blocks of memory
724+
/// 4. Memory fragmentation - By using homogeneous buffer sizes, it ensures that blocks of memory
726725
/// can be easily reused.
727726
///
728727
/// The stream is implemented on top of a series of uniformly-sized blocks. As the stream's length grows,
@@ -822,9 +821,8 @@ internal RecyclableMemoryStreamManager MemoryManager
822821
/// </summary>
823822
/// <param name="memoryManager">The memory manager</param>
824823
public RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager)
825-
: this(memoryManager, null)
824+
: this(memoryManager, null, 0, null)
826825
{
827-
828826
}
829827

830828
/// <summary>
@@ -833,9 +831,8 @@ public RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager)
833831
/// <param name="memoryManager">The memory manager</param>
834832
/// <param name="tag">A string identifying this stream for logging and debugging purposes</param>
835833
public RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager, string tag)
836-
: this(memoryManager, tag, 0)
834+
: this(memoryManager, tag, 0, null)
837835
{
838-
839836
}
840837

841838
/// <summary>
@@ -890,7 +887,6 @@ internal RecyclableMemoryStream(RecyclableMemoryStreamManager memoryManager, str
890887
#endregion
891888

892889
#region Dispose and Finalize
893-
894890
~RecyclableMemoryStream()
895891
{
896892
this.Dispose(false);
@@ -913,10 +909,7 @@ protected override void Dispose(bool disposing)
913909
}
914910

915911
Events.Write.MemoryStreamDoubleDispose(this.id, this.tag, this.allocationStack, this.disposeStack, doubleDisposeStack);
916-
if (MemoryStreamFactory.MuteDuplicateDisposeExceptions)
917-
return;
918-
919-
throw new InvalidOperationException("Cannot dispose of RecyclableMemoryStream twice");
912+
return;
920913
}
921914

922915
Events.Write.MemoryStreamDisposed(this.id, this.tag);
@@ -941,7 +934,6 @@ protected override void Dispose(bool disposing)
941934

942935
Events.Write.MemoryStreamFinalized(this.id, this.tag, this.allocationStack);
943936

944-
#if !PCL
945937
if (AppDomain.CurrentDomain.IsFinalizingForUnload())
946938
{
947939
// If we're being finalized because of a shutdown, don't go any further.
@@ -950,7 +942,6 @@ protected override void Dispose(bool disposing)
950942
base.Dispose(disposing);
951943
return;
952944
}
953-
#endif
954945

955946
this.memoryManager.ReportStreamFinalized();
956947
}
@@ -986,7 +977,6 @@ public void Close()
986977
{
987978
this.Dispose(true);
988979
}
989-
990980
#endregion
991981

992982
#region MemoryStream overrides
@@ -1016,7 +1006,11 @@ public override int Capacity
10161006
}
10171007
return 0;
10181008
}
1019-
set { this.EnsureCapacity(value); }
1009+
set
1010+
{
1011+
this.CheckDisposed();
1012+
this.EnsureCapacity(value);
1013+
}
10201014
}
10211015

10221016
private int length;
@@ -1049,6 +1043,7 @@ public override long Position
10491043
}
10501044
set
10511045
{
1046+
this.CheckDisposed();
10521047
if (value < 0)
10531048
{
10541049
throw new ArgumentOutOfRangeException("value", "value must be non-negative");
@@ -1125,7 +1120,7 @@ public byte[] GetBuffer()
11251120
// it's possible that people will manipulate the buffer directly
11261121
// and set the length afterward. Capacity sets the expectation
11271122
// for the size of the buffer.
1128-
var newBuffer = this.MemoryManager.GetLargeBuffer(this.Capacity, this.tag);
1123+
var newBuffer = this.memoryManager.GetLargeBuffer(this.Capacity, this.tag);
11291124

11301125
// InternalRead will check for existence of largeBuffer, so make sure we
11311126
// don't set it until after we've copied the data.
@@ -1147,6 +1142,7 @@ public byte[] GetBuffer()
11471142
/// for the sake of completeness.
11481143
/// </summary>
11491144
/// <exception cref="ObjectDisposedException">Object has been disposed</exception>
1145+
[Obsolete("This method has degraded performance vs. GetBuffer and should be avoided.")]
11501146
public override byte[] ToArray()
11511147
{
11521148
this.CheckDisposed();
@@ -1232,53 +1228,49 @@ public override void Write(byte[] buffer, int offset, int count)
12321228
throw new ArgumentException("count must be greater than buffer.Length - offset");
12331229
}
12341230

1231+
int blockSize = this.memoryManager.BlockSize;
1232+
long end = (long)this.position + count;
12351233
// Check for overflow
1236-
if (this.Position + count > MaxStreamLength)
1234+
if (end > MaxStreamLength)
12371235
{
12381236
throw new IOException("Maximum capacity exceeded");
12391237
}
12401238

1241-
int end = (int)this.Position + count;
1242-
1243-
int blockSize = this.memoryManager.BlockSize;
1244-
1245-
int requiredBuffers = (end + blockSize - 1) / blockSize;
1239+
long requiredBuffers = (end + blockSize - 1) / blockSize;
12461240

12471241
if (requiredBuffers * blockSize > MaxStreamLength)
12481242
{
12491243
throw new IOException("Maximum capacity exceeded");
12501244
}
12511245

1252-
EnsureCapacity(end);
1246+
this.EnsureCapacity((int)end);
12531247

12541248
if (this.largeBuffer == null)
12551249
{
12561250
int bytesRemaining = count;
12571251
int bytesWritten = 0;
1258-
int currentBlockIndex = this.OffsetToBlockIndex(this.position);
1259-
1260-
int blockOffset = this.OffsetToBlockOffset(this.position);
1252+
var blockAndOffset = this.GetBlockAndRelativeOffset(this.position);
12611253

12621254
while (bytesRemaining > 0)
12631255
{
1264-
byte[] currentBlock = this.blocks[currentBlockIndex];
1265-
int remainingInBlock = blockSize - blockOffset;
1256+
byte[] currentBlock = this.blocks[blockAndOffset.Block];
1257+
int remainingInBlock = blockSize - blockAndOffset.Offset;
12661258
int amountToWriteInBlock = Math.Min(remainingInBlock, bytesRemaining);
12671259

1268-
Buffer.BlockCopy(buffer, offset + bytesWritten, currentBlock, blockOffset, amountToWriteInBlock);
1260+
Buffer.BlockCopy(buffer, offset + bytesWritten, currentBlock, blockAndOffset.Offset, amountToWriteInBlock);
12691261

12701262
bytesRemaining -= amountToWriteInBlock;
12711263
bytesWritten += amountToWriteInBlock;
12721264

1273-
currentBlockIndex++;
1274-
blockOffset = 0;
1265+
++blockAndOffset.Block;
1266+
blockAndOffset.Offset = 0;
12751267
}
12761268
}
12771269
else
12781270
{
12791271
Buffer.BlockCopy(buffer, offset, this.largeBuffer, this.position, count);
12801272
}
1281-
this.Position = end;
1273+
this.position = (int)end;
12821274
this.length = Math.Max(this.position, this.length);
12831275
}
12841276

@@ -1297,7 +1289,7 @@ public override string ToString()
12971289
/// <exception cref="ObjectDisposedException">Object has been disposed</exception>
12981290
public override void WriteByte(byte value)
12991291
{
1300-
CheckDisposed();
1292+
this.CheckDisposed();
13011293
this.byteBuffer[0] = value;
13021294
this.Write(this.byteBuffer, 0, 1);
13031295
}
@@ -1317,9 +1309,8 @@ public override int ReadByte()
13171309
byte value = 0;
13181310
if (this.largeBuffer == null)
13191311
{
1320-
int block = OffsetToBlockIndex(this.position);
1321-
int blockOffset = OffsetToBlockOffset(this.position);
1322-
value = this.blocks[block][blockOffset];
1312+
var blockAndOffset = this.GetBlockAndRelativeOffset(this.position);
1313+
value = this.blocks[blockAndOffset.Block][blockAndOffset.Offset];
13231314
}
13241315
else
13251316
{
@@ -1399,6 +1390,7 @@ public override long Seek(long offset, SeekOrigin loc)
13991390
/// <remarks>Important: This does a synchronous write, which may not be desired in some situations</remarks>
14001391
public override void WriteTo(Stream stream)
14011392
{
1393+
this.CheckDisposed();
14021394
if (stream == null)
14031395
{
14041396
throw new ArgumentNullException("stream");
@@ -1427,7 +1419,6 @@ public override void WriteTo(Stream stream)
14271419
#endregion
14281420

14291421
#region Helper Methods
1430-
14311422
private void CheckDisposed()
14321423
{
14331424
if (this.disposed)
@@ -1444,21 +1435,20 @@ private int InternalRead(byte[] buffer, int offset, int count, int fromPosition)
14441435
}
14451436
if (this.largeBuffer == null)
14461437
{
1447-
int currentBlock = this.OffsetToBlockIndex(fromPosition);
1438+
var blockAndOffset = this.GetBlockAndRelativeOffset(fromPosition);
14481439
int bytesWritten = 0;
14491440
int bytesRemaining = Math.Min(count, this.length - fromPosition);
1450-
int blockOffset = this.OffsetToBlockOffset(fromPosition);
14511441

14521442
while (bytesRemaining > 0)
14531443
{
1454-
int amountToCopy = Math.Min(this.blocks[currentBlock].Length - blockOffset, bytesRemaining);
1455-
Buffer.BlockCopy(this.blocks[currentBlock], blockOffset, buffer, bytesWritten + offset, amountToCopy);
1444+
int amountToCopy = Math.Min(this.blocks[blockAndOffset.Block].Length - blockAndOffset.Offset, bytesRemaining);
1445+
Buffer.BlockCopy(this.blocks[blockAndOffset.Block], blockAndOffset.Offset, buffer, bytesWritten + offset, amountToCopy);
14561446

14571447
bytesWritten += amountToCopy;
14581448
bytesRemaining -= amountToCopy;
14591449

1460-
++currentBlock;
1461-
blockOffset = 0;
1450+
++blockAndOffset.Block;
1451+
blockAndOffset.Offset = 0;
14621452
}
14631453
return bytesWritten;
14641454
}
@@ -1470,14 +1460,22 @@ private int InternalRead(byte[] buffer, int offset, int count, int fromPosition)
14701460
}
14711461
}
14721462

1473-
private int OffsetToBlockIndex(int offset)
1463+
private struct BlockAndOffset
14741464
{
1475-
return offset / this.memoryManager.BlockSize;
1465+
public int Block;
1466+
public int Offset;
1467+
1468+
public BlockAndOffset(int block, int offset)
1469+
{
1470+
this.Block = block;
1471+
this.Offset = offset;
1472+
}
14761473
}
14771474

1478-
private int OffsetToBlockOffset(int offset)
1475+
private BlockAndOffset GetBlockAndRelativeOffset(int offset)
14791476
{
1480-
return offset % this.memoryManager.BlockSize;
1477+
var blockSize = this.memoryManager.BlockSize;
1478+
return new BlockAndOffset(offset / blockSize, offset % blockSize);
14811479
}
14821480

14831481
private void EnsureCapacity(int newCapacity)
@@ -1502,7 +1500,7 @@ private void EnsureCapacity(int newCapacity)
15021500
{
15031501
while (this.Capacity < newCapacity)
15041502
{
1505-
blocks.Add((this.MemoryManager.GetBlock()));
1503+
blocks.Add((this.memoryManager.GetBlock()));
15061504
}
15071505
}
15081506
}

0 commit comments

Comments
 (0)