using System; using System.Collections.Generic; using System.IO; using System.Text; namespace MpcNET { /// /// /// public class MpdResponseReader : IDisposable { /// /// /// public enum NextData { #pragma warning disable CS1591 Unknown, Eof, String, BinaryData #pragma warning restore CS1591 } /// /// Length of the binary part of the response. /// public int binaryEnd; private Stream source; private Encoding encoding; private byte[] byteBuffer; private int bufferOffset; private int bufferEnd; private NextData nextData; private int binaryOffset; private char[] characterBuffer; /// /// /// /// /// public MpdResponseReader(Stream source, Encoding encoding) { this.source = source; this.encoding = encoding; } /// /// /// /// public NextData ReportNextData() { if (nextData != NextData.Unknown) { return nextData; } if (!PopulateBufferIfNeeded(1)) { return (nextData = NextData.Eof); } return (nextData = NextData.String); } /// /// /// /// public string ReadString() { ReportNextData(); if (nextData == NextData.Eof) { throw new EndOfStreamException(); } else if (nextData != NextData.String) { throw new InvalidOperationException("Attempt to read non-string data as string"); } if (characterBuffer == null) { characterBuffer = new char[4]; } StringBuilder stringBuilder = new StringBuilder(); Decoder decoder = encoding.GetDecoder(); while (nextData == NextData.String) { byte b = byteBuffer[bufferOffset]; if (b == '\n') { // Analyze the obtained string to see if it's a binary data header if (stringBuilder.ToString().StartsWith(Constants.Binary)) { nextData = NextData.BinaryData; binaryEnd = int.Parse(stringBuilder.ToString().Replace(Constants.Binary, "")); binaryOffset = 0; } else { nextData = NextData.Unknown; } bufferOffset++; break; } else { var decodedChars = decoder.GetChars(byteBuffer, bufferOffset++, 1, characterBuffer, 0); if (decodedChars == 1) { stringBuilder.Append(characterBuffer[0]); } else { stringBuilder.Append(characterBuffer, 0, decodedChars); } if (bufferOffset == bufferEnd && !PopulateBufferIfNeeded(1)) { nextData = NextData.Eof; break; } } } return stringBuilder.ToString(); } /// /// /// /// /// /// /// public int ReadBinaryData(byte[] buffer, int offset, int count) { ReportNextData(); if (nextData == NextData.Eof) { throw new EndOfStreamException(); } else if (nextData != NextData.BinaryData) { throw new InvalidOperationException("Attempt to read non-binary data as binary data"); } if (count > binaryEnd - binaryOffset) { count = binaryEnd - binaryOffset; //throw new EndOfStreamException(); } int bytesRead; if (bufferOffset < bufferEnd) { bytesRead = Math.Min(count, bufferEnd - bufferOffset); Array.Copy(byteBuffer, bufferOffset, buffer, offset, bytesRead); bufferOffset += bytesRead; } else if (count < byteBuffer.Length) { if (!PopulateBufferIfNeeded(1)) { throw new EndOfStreamException(); } bytesRead = Math.Min(count, bufferEnd - bufferOffset); Array.Copy(byteBuffer, bufferOffset, buffer, offset, bytesRead); bufferOffset += bytesRead; } else { bytesRead = source.Read(buffer, offset, count); } binaryOffset += bytesRead; if (binaryOffset == binaryEnd) { nextData = NextData.Unknown; } return bytesRead; } private bool PopulateBufferIfNeeded(int minimumBytes) { if (byteBuffer == null) { byteBuffer = new byte[32768]; } if (bufferEnd - bufferOffset < minimumBytes) { int shiftCount = bufferEnd - bufferOffset; if (shiftCount > 0) { Array.Copy(byteBuffer, bufferOffset, byteBuffer, 0, shiftCount); } bufferOffset = 0; bufferEnd = shiftCount; while (bufferEnd - bufferOffset < minimumBytes) { int bytesRead = source.Read(byteBuffer, bufferEnd, byteBuffer.Length - bufferEnd); if (bytesRead == 0) { return false; } bufferEnd += bytesRead; } } return true; } /// /// /// public void Dispose() { Stream source = this.source; this.source = null; if (source != null) { source.Dispose(); } } } }