e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
371 lines
13 KiB
C#
371 lines
13 KiB
C#
#if false //deadcode
|
|
|
|
//------------------------------------------------------------------------------
|
|
// <copyright file="ScatterGatherStream.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Web.Services.Protocols {
|
|
using System;
|
|
using System.IO;
|
|
using System.Diagnostics;
|
|
|
|
internal class ScatterGatherStream : Stream {
|
|
private const int MemStreamMaxLength = Int32.MaxValue;
|
|
|
|
private MemoryChunk headChunk = null;
|
|
private MemoryChunk currentChunk = null;
|
|
|
|
private long chunkSize = 0;
|
|
private int currentOffset = 0;
|
|
private int endOffset = 0;
|
|
private long currentChunkStartPos = 0;
|
|
|
|
internal ScatterGatherStream(int chunkSize) {
|
|
this.chunkSize = chunkSize;
|
|
currentChunk = headChunk = AllocateMemoryChunk(this.chunkSize);
|
|
currentOffset = endOffset = 0;
|
|
currentChunkStartPos = 0;
|
|
}
|
|
|
|
internal ScatterGatherStream() : this(1024) { }
|
|
|
|
public override bool CanRead { get { return true; } }
|
|
public override bool CanSeek { get { return true; } }
|
|
public override bool CanWrite { get { return true; } }
|
|
|
|
public override void Close() {
|
|
headChunk = null;
|
|
currentChunk = null;
|
|
endOffset = currentOffset = 0;
|
|
currentChunkStartPos = 0;
|
|
}
|
|
|
|
public override void Flush() { }
|
|
|
|
public override long Length {
|
|
get {
|
|
MemoryChunk endChunk;
|
|
return GetLengthInternal(out endChunk);
|
|
}
|
|
}
|
|
|
|
private long GetLengthInternal(out MemoryChunk endChunk){
|
|
long length = currentChunkStartPos;
|
|
MemoryChunk chunk = currentChunk;
|
|
while (chunk.Next != null) {
|
|
length += chunk.Buffer.Length;
|
|
chunk = chunk.Next;
|
|
}
|
|
length += endOffset;
|
|
endChunk = chunk;
|
|
return length;
|
|
}
|
|
|
|
public override long Position {
|
|
get {
|
|
return Seek(0, SeekOrigin.Current);
|
|
}
|
|
|
|
set {
|
|
Seek(value, SeekOrigin.Begin);
|
|
}
|
|
}
|
|
|
|
|
|
public override long Seek(long offset, SeekOrigin loc) {
|
|
MemoryChunk chunk = null;;
|
|
long relativeOffset = 0;
|
|
long absoluteOffset = 0;
|
|
|
|
if(loc == SeekOrigin.Begin){
|
|
absoluteOffset = offset;
|
|
if(offset >= currentChunkStartPos){
|
|
chunk = currentChunk;
|
|
relativeOffset = offset - currentChunkStartPos;
|
|
}
|
|
else{
|
|
chunk = headChunk;
|
|
relativeOffset = absoluteOffset;
|
|
}
|
|
}
|
|
else if( loc == SeekOrigin.Current){
|
|
absoluteOffset = offset + currentOffset + currentChunkStartPos;
|
|
if( (offset + currentOffset) > 0){
|
|
chunk = currentChunk;
|
|
relativeOffset = offset + currentOffset;
|
|
}
|
|
else {
|
|
chunk = headChunk;
|
|
relativeOffset = absoluteOffset;
|
|
}
|
|
}
|
|
else if (loc == SeekOrigin.End){
|
|
MemoryChunk endChunk;
|
|
long length = GetLengthInternal(out endChunk);
|
|
absoluteOffset = offset + length;
|
|
if ( (offset + endOffset) > 0 ) {
|
|
relativeOffset = offset + endOffset;
|
|
chunk = endChunk;
|
|
}
|
|
else if(absoluteOffset >= currentChunkStartPos){
|
|
chunk = currentChunk;
|
|
relativeOffset = absoluteOffset - currentChunkStartPos;
|
|
}
|
|
else {
|
|
chunk = headChunk;
|
|
relativeOffset = absoluteOffset;
|
|
}
|
|
}
|
|
else
|
|
throw new ArgumentOutOfRangeException("loc");
|
|
|
|
if (relativeOffset < 0 || relativeOffset > MemStreamMaxLength)
|
|
throw new ArgumentOutOfRangeException("offset");
|
|
long remaining = relativeOffset;
|
|
while (chunk.Next != null) {
|
|
if (remaining < chunk.Buffer.Length){
|
|
currentChunk = chunk;
|
|
currentOffset = (int)remaining;
|
|
currentChunkStartPos = absoluteOffset - currentOffset;
|
|
remaining = -1;
|
|
break;
|
|
}
|
|
remaining -= chunk.Buffer.Length;
|
|
chunk = chunk.Next;
|
|
}
|
|
|
|
if (remaining >= 0){
|
|
if (remaining <= chunk.Buffer.Length)
|
|
currentChunk = chunk;
|
|
else {
|
|
currentChunk = chunk.Next = AllocateMemoryChunk(2*remaining);
|
|
endOffset = 0;
|
|
}
|
|
currentOffset = (int)remaining;
|
|
currentChunkStartPos = absoluteOffset - currentOffset;
|
|
SyncEndOffset();
|
|
}
|
|
|
|
return absoluteOffset;
|
|
}
|
|
|
|
public override void SetLength(long absNewLen) {
|
|
if (absNewLen < 0 || absNewLen > MemStreamMaxLength)
|
|
throw new ArgumentOutOfRangeException("offset");
|
|
|
|
MemoryChunk chunk;
|
|
bool currentPastEnd;
|
|
long relNewLen;
|
|
if(absNewLen >= currentChunkStartPos){
|
|
currentPastEnd = false;
|
|
chunk = currentChunk;
|
|
relNewLen = absNewLen - currentChunkStartPos;
|
|
}
|
|
else {
|
|
currentPastEnd = true;
|
|
chunk = headChunk;
|
|
relNewLen = absNewLen;
|
|
}
|
|
long startPos = 0;
|
|
MemoryChunk endChunk = null;
|
|
while (chunk != null) {
|
|
long endPos = startPos + chunk.Buffer.Length;
|
|
if(endPos > relNewLen){
|
|
chunk.Next = null;
|
|
endOffset = (int)(relNewLen - startPos);
|
|
if(chunk == currentChunk)
|
|
currentOffset = min(currentOffset, endOffset);
|
|
else if(currentPastEnd){
|
|
currentChunk = chunk;
|
|
currentOffset = endOffset;
|
|
currentChunkStartPos = absNewLen - currentOffset;
|
|
}
|
|
return;
|
|
}
|
|
startPos = endPos;
|
|
endChunk = chunk;
|
|
chunk = chunk.Next;
|
|
}
|
|
//assert(endChunk != null)
|
|
endChunk.Next = AllocateMemoryChunk((int)(absNewLen - startPos));
|
|
endOffset = (int)(absNewLen - startPos);
|
|
}
|
|
|
|
|
|
|
|
public override int Read(byte[] buffer, int offset, int count) {
|
|
byte[] chunkBuffer = currentChunk.Buffer;
|
|
int chunkSize = chunkBuffer.Length;
|
|
if (currentChunk.Next == null)
|
|
chunkSize = endOffset;
|
|
|
|
int bytesRead = 0;
|
|
|
|
while (count > 0) {
|
|
if (currentOffset == chunkSize) {
|
|
// exit if no more chunks are currently available
|
|
if (currentChunk.Next == null)
|
|
break;
|
|
|
|
currentChunkStartPos += currentChunk.Buffer.Length;
|
|
currentChunk = currentChunk.Next;
|
|
currentOffset = 0;
|
|
chunkBuffer = currentChunk.Buffer;
|
|
chunkSize = chunkBuffer.Length;
|
|
if (currentChunk.Next == null)
|
|
chunkSize = endOffset;
|
|
}
|
|
|
|
int readCount = min(count, chunkSize - currentOffset);
|
|
Buffer.BlockCopy(chunkBuffer, currentOffset, buffer, offset, readCount);
|
|
offset += readCount;
|
|
count -= readCount;
|
|
currentOffset += readCount;
|
|
bytesRead += readCount;
|
|
}
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
byte[] oneByteBuffer = new byte[1];
|
|
public override int ReadByte(){
|
|
if(Read(oneByteBuffer, 0, 1) == 1)
|
|
return oneByteBuffer[0];
|
|
return -1;
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count) {
|
|
byte[] chunkBuffer = currentChunk.Buffer;
|
|
int chunkSize = chunkBuffer.Length;
|
|
|
|
while (count > 0) {
|
|
if (currentOffset == chunkSize) {
|
|
// allocate a new chunk if the current one is full
|
|
if(currentChunk.Next == null){
|
|
currentChunk.Next = AllocateMemoryChunk(count);
|
|
endOffset = 0;
|
|
}
|
|
currentChunkStartPos += currentChunk.Buffer.Length;
|
|
currentChunk = currentChunk.Next;
|
|
currentOffset = 0;
|
|
|
|
chunkBuffer = currentChunk.Buffer;
|
|
chunkSize = chunkBuffer.Length;
|
|
}
|
|
|
|
int copyCount = min(count, chunkSize - endOffset);
|
|
Buffer.BlockCopy(buffer, offset, chunkBuffer, endOffset, copyCount);
|
|
offset += copyCount;
|
|
count -= copyCount;
|
|
currentOffset += copyCount;
|
|
SyncEndOffset();
|
|
}
|
|
}
|
|
|
|
public override void WriteByte(byte value) {
|
|
oneByteBuffer[0] = value;
|
|
Write(oneByteBuffer, 0, 1);
|
|
}
|
|
|
|
internal bool GetNextBuffer(out byte[] buffer, out int byteOffset, out int byteCount) {
|
|
buffer = null;
|
|
byteOffset = 0;
|
|
byteCount = 0;
|
|
if (currentChunk == null || headChunk == null || (currentChunk.Next == null && currentOffset == endOffset))
|
|
return false;
|
|
|
|
buffer = currentChunk.Buffer;
|
|
if (currentChunk.Next == null) {
|
|
byteCount = endOffset;
|
|
currentOffset = endOffset;
|
|
}
|
|
else {
|
|
currentChunkStartPos += currentChunk.Buffer.Length;
|
|
currentChunk = currentChunk.Next;
|
|
byteCount = buffer.Length;
|
|
currentOffset = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// copy entire buffer into an array
|
|
internal virtual byte[] ToArray() {
|
|
int length = (int)Length; // this will throw if stream is closed
|
|
byte[] copy = new byte[length];
|
|
|
|
MemoryChunk backupReadChunk = currentChunk;
|
|
int backupReadOffset = currentOffset;
|
|
|
|
currentChunk = headChunk;
|
|
currentOffset = 0;
|
|
Read(copy, 0, length);
|
|
|
|
currentChunk = backupReadChunk;
|
|
currentOffset = backupReadOffset;
|
|
|
|
return copy;
|
|
}
|
|
|
|
|
|
// write remainder of this stream to another stream
|
|
internal virtual void WriteTo(Stream stream) {
|
|
if (stream == null)
|
|
throw new ArgumentNullException("stream");
|
|
|
|
byte[] chunkBuffer = currentChunk.Buffer;
|
|
int chunkSize = chunkBuffer.Length;
|
|
if (currentChunk.Next == null)
|
|
chunkSize = endOffset;
|
|
|
|
// following code mirrors Read() logic (currentChunk/currentOffset should
|
|
// point just past last byte of last chunk when done)
|
|
|
|
for (;;){ // loop until end of chunks is found
|
|
if (currentOffset == chunkSize) {
|
|
// exit if no more chunks are currently available
|
|
if (currentChunk.Next == null)
|
|
break;
|
|
|
|
currentChunkStartPos += currentChunk.Buffer.Length;
|
|
currentChunk = currentChunk.Next;
|
|
currentOffset = 0;
|
|
chunkBuffer = currentChunk.Buffer;
|
|
chunkSize = chunkBuffer.Length;
|
|
if (currentChunk.Next == null)
|
|
chunkSize = endOffset;
|
|
}
|
|
|
|
int writeCount = chunkSize - currentOffset;
|
|
stream.Write(chunkBuffer, currentOffset, writeCount);
|
|
currentOffset = chunkSize;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int min(int a, int b) { return a < b ? a : b;}
|
|
|
|
private MemoryChunk AllocateMemoryChunk(long newSize) {
|
|
if(newSize > chunkSize) chunkSize = newSize;
|
|
MemoryChunk chunk = new MemoryChunk();
|
|
chunk.Buffer = new byte[chunkSize];
|
|
chunkSize*=2;//nexttime alloc more
|
|
chunk.Next = null;
|
|
return chunk;
|
|
}
|
|
|
|
private void SyncEndOffset() {
|
|
if (currentChunk.Next == null && currentOffset > endOffset)
|
|
endOffset = currentOffset;
|
|
}
|
|
|
|
private class MemoryChunk {
|
|
internal byte[] Buffer = null;
|
|
internal MemoryChunk Next = null;
|
|
}
|
|
}
|
|
}
|
|
#endif |