/// <summary> /// Drains chunks to the given stream and notifies the given promise when it's done. /// </summary> /// <param name="stream"></param> /// <param name="promise"></param> private void DrainToStream(Stream stream, Promise <ChunkedBuffer> promise) { MemoryChunkNode currentChunk = null; lock (_syncRoot) { currentChunk = rootChunk; if (currentChunk == null) { promise.CreateFulfiller().Fulfill(this); return; } rootChunk = rootChunk.next; } stream.BeginWrite(currentChunk.pooledBytes, currentChunk.offset, currentChunk.count, new AsyncCallback(OnDrainToStreamWriteComplete), new DrainChunksState() { currentChunk = currentChunk, stream = stream, promise = promise }); }
/// <summary> /// Appeds the given chunk to this stream. /// </summary> /// <param name="chunk"></param> private void AppendChunk(MemoryChunkNode chunk) { lock (_syncRoot) { if (rootChunk == null) { rootChunk = chunk; } else { MemoryChunkNode currentChunk = rootChunk; while (currentChunk != null) { if (currentChunk.next == null) { currentChunk.next = chunk; break; } else { currentChunk = currentChunk.next; } } } WritePosition += chunk.count; } }
/// <summary> /// Affers a pooled memory chunk to this stream. /// </summary> /// <param name="pooledBytes"></param> /// <param name="offset"></param> /// <param name="length"></param> public ChunkedBuffer OfferChunk(PooledObject <byte[]> pooledObject, int offset, int count) { ValidateBuffer(); if (pooledObject == null) { throw new ArgumentNullException("'data' cannot be null"); } if (pooledObject.State != PooledObjectState.USED) { throw new Exception("This pooled object is not active."); } if (pooledObject.Pool != pool) { throw new Exception("The given pooled object does not belong to the pool that is assigned to this stream: " + pooledObject.Pool); } MemoryChunkNode chunk = new MemoryChunkNode() { pooledObject = pooledObject, pooledBytes = pooledObject.Value, offset = offset, count = count, next = null }; pooledObject.RefCount.Increment(); AppendChunk(chunk); return(this); }
/// <summary> /// Flushes this stream and clears any read pooled memory chunks. /// </summary> public void Flush() { lock (_syncRoot) { ValidateBuffer(); MemoryChunkNode currentChunk = rootChunk; while (currentChunk != null) { if (currentChunk.count > ReadPosition) { break; } if (currentChunk.pooledObject != null && currentChunk.pooledObject.RefCount.Decrement() < 1) { if (currentChunk.pooledObject.State == PooledObjectState.USED) { currentChunk.pooledObject.Return(); } else { SockNetLogger.Log(SockNetLogger.LogLevel.WARN, this, "Potential resource leak found."); } } rootChunk = currentChunk.next; ReadPosition -= currentChunk.count; WritePosition = Math.Max(0, WritePosition - currentChunk.count); currentChunk = currentChunk.next; } } }
/// <summary> /// Reads data into the given buffer from the current position. /// </summary> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="length"></param> /// <returns></returns> public int Read(byte[] buffer, int offset, int count) { lock (_syncRoot) { ValidateBuffer(); if (WritePosition == ReadPosition) { return(0); } int bytesScanned = 0; int bytesRead = 0; MemoryChunkNode currentChunk = rootChunk; while (currentChunk != null && bytesRead < count) { if (bytesScanned >= ReadPosition) { int bytesToCopy = Math.Min(currentChunk.count, count - bytesRead); Buffer.BlockCopy(currentChunk.pooledBytes, currentChunk.offset, buffer, offset + bytesRead, bytesToCopy); bytesRead += bytesToCopy; } else if (currentChunk.count + bytesScanned >= ReadPosition) { int sourceChunkOffset = (int)(ReadPosition - bytesScanned); int bytesToCopy = Math.Min(currentChunk.count - sourceChunkOffset, count - bytesRead); Buffer.BlockCopy(currentChunk.pooledBytes, currentChunk.offset + sourceChunkOffset, buffer, offset + bytesRead, bytesToCopy); bytesRead += bytesToCopy; } bytesScanned += currentChunk.count; currentChunk = currentChunk.next; } ReadPosition += bytesRead; return(bytesRead); } }
/// <summary> /// Synchronously drains the buffer into the stream. /// </summary> /// <param name="stream"></param> public ChunkedBuffer DrainToStreamSync(Stream stream) { lock (_syncRoot) { while (rootChunk != null) { stream.Write(rootChunk.pooledBytes, rootChunk.offset, rootChunk.count); if (rootChunk.pooledObject != null && rootChunk.pooledObject.Pool != null && rootChunk.pooledObject.RefCount.Decrement() < 1) { rootChunk.pooledObject.Return(); } rootChunk = rootChunk.next; } } return(this); }
/// <summary> /// Offers the following raw buffer. /// </summary> /// <param name="data"></param> public ChunkedBuffer OfferRaw(byte[] data, int offset, int count) { ValidateBuffer(); if (data == null) { throw new ArgumentNullException("'data' cannot be null"); } MemoryChunkNode chunk = new MemoryChunkNode() { pooledObject = null, pooledBytes = data, offset = offset, count = count, next = null }; AppendChunk(chunk); return(this); }
/// <summary> /// Drains chunks to the given stream and notifies the given promise when it's done. /// </summary> /// <param name="stream"></param> /// <param name="promise"></param> private void DrainToStream(Stream stream, Promise<ChunkedBuffer> promise) { MemoryChunkNode currentChunk = null; lock (_syncRoot) { currentChunk = rootChunk; if (currentChunk == null) { promise.CreateFulfiller().Fulfill(this); return; } rootChunk = rootChunk.next; } stream.BeginWrite(currentChunk.pooledBytes, currentChunk.offset, currentChunk.count, new AsyncCallback(OnDrainToStreamWriteComplete), new DrainChunksState() { currentChunk = currentChunk, stream = stream, promise = promise }); }
/// <summary> /// Offers the following raw buffer. /// </summary> /// <param name="data"></param> public ChunkedBuffer OfferRaw(byte[] data, int offset, int count) { ValidateBuffer(); if (data == null) { throw new ArgumentNullException("'data' cannot be null"); } MemoryChunkNode chunk = new MemoryChunkNode() { pooledObject = null, pooledBytes = data, offset = offset, count = count, next = null }; AppendChunk(chunk); return this; }
/// <summary> /// Affers a pooled memory chunk to this stream. /// </summary> /// <param name="pooledBytes"></param> /// <param name="offset"></param> /// <param name="length"></param> public ChunkedBuffer OfferChunk(PooledObject<byte[]> pooledObject, int offset, int count) { ValidateBuffer(); if (pooledObject == null) { throw new ArgumentNullException("'data' cannot be null"); } if (pooledObject.State != PooledObjectState.USED) { throw new Exception("This pooled object is not active."); } if (pooledObject.Pool != pool) { throw new Exception("The given pooled object does not belong to ths pool that is assigned to this stream: " + pooledObject.Pool); } MemoryChunkNode chunk = new MemoryChunkNode() { pooledObject = pooledObject, pooledBytes = pooledObject.Value, offset = offset, count = count, next = null }; pooledObject.RefCount.Increment(); AppendChunk(chunk); return this; }
/// <summary> /// Flushes this stream and clears any read pooled memory chunks. /// </summary> public void Flush() { lock (_syncRoot) { ValidateBuffer(); MemoryChunkNode currentChunk = rootChunk; while (currentChunk != null) { if (currentChunk.count > ReadPosition) { break; } if (currentChunk.pooledObject != null && currentChunk.pooledObject.RefCount.Decrement() < 1) { if (currentChunk.pooledObject.State == PooledObjectState.USED) { currentChunk.pooledObject.Return(); } else { SockNetLogger.Log(SockNetLogger.LogLevel.WARN, this, "[Flush] Potential resource leak found."); } } rootChunk = currentChunk.next; ReadPosition -= currentChunk.count; WritePosition = Math.Max(0, WritePosition - currentChunk.count); currentChunk = currentChunk.next; } } }
/// <summary> /// Synchronously drains the buffer into the stream. /// </summary> /// <param name="stream"></param> public ChunkedBuffer DrainToStreamSync(Stream stream) { lock (_syncRoot) { while (rootChunk != null) { stream.Write(rootChunk.pooledBytes, rootChunk.offset, rootChunk.count); if (rootChunk.pooledObject != null && rootChunk.pooledObject.Pool != null && rootChunk.pooledObject.RefCount.Decrement() < 1) { rootChunk.pooledObject.Return(); } rootChunk = rootChunk.next; } } return this; }