using System; using System.Text; namespace Shadowsocks.Util.Sockets { public class LineReader { private readonly WrappedSocket _socket; private readonly Func _onLineRead; private readonly Action _onException; private readonly Action _onFinish; private readonly Encoding _encoding; // private readonly string _delimiter; private readonly byte[] _delimiterBytes; private readonly ByteSearch.SearchTarget _delimiterSearch; private readonly object _state; private readonly byte[] _lineBuffer; private int _bufferDataIndex; private int _bufferDataLength; public LineReader(byte[] buffer, WrappedSocket socket, byte[] firstPackge, int index, int length, Func onLineRead, Action onException, Action onFinish, Encoding encoding, string delimiter, object state) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (socket == null) { throw new ArgumentNullException(nameof(socket)); } if (onLineRead == null) { throw new ArgumentNullException(nameof(onLineRead)); } if (encoding == null) { throw new ArgumentNullException(nameof(encoding)); } if (delimiter == null) { throw new ArgumentNullException(nameof(delimiter)); } if (buffer.Length < length) { throw new ArgumentException("Line buffer length can't less than first package length!", nameof(buffer)); } if (length > 0) { if (firstPackge == null) { throw new ArgumentNullException(nameof(firstPackge)); } } _socket = socket; _onLineRead = onLineRead; _onException = onException; _onFinish = onFinish; _encoding = encoding; // _delimiter = delimiter; _state = state; // decode delimiter _delimiterBytes = encoding.GetBytes(delimiter); if (_delimiterBytes.Length == 0) { throw new ArgumentException("Too short!", nameof(delimiter)); } if (buffer.Length < _delimiterBytes.Length) { throw new ArgumentException("Too small!", nameof(buffer)); } _delimiterSearch = new ByteSearch.SearchTarget(_delimiterBytes); _lineBuffer = buffer; if (length > 0) { // process first package if (buffer == firstPackge) { _bufferDataIndex = index; } else { Array.Copy(firstPackge, index, _lineBuffer, 0, length); } _bufferDataLength = length; try { NewPackageRecv(); } catch (Exception ex) { OnException(ex); OnFinish(); } } else { // start reading socket.BeginReceive(_lineBuffer, 0, _lineBuffer.Length, 0, ReceiveCallback, 0); } } public LineReader(int maxLineBytes, WrappedSocket socket, Func onLineRead, Action onException, Action onFinish, Encoding encoding, string delimiter, object state) : this( new byte[maxLineBytes], socket, null, 0, 0, onLineRead, onException, onFinish, encoding, delimiter, state) { } private void ReceiveCallback(IAsyncResult ar) { try { var bytesRead = _socket.EndReceive(ar); if (bytesRead == 0) { OnFinish(); return; } _bufferDataLength += bytesRead; NewPackageRecv(); } catch (Exception ex) { OnException(ex); OnFinish(); } } private void NewPackageRecv() { int i; while ((i = _delimiterSearch.SearchIn(_lineBuffer, _bufferDataIndex, _bufferDataLength)) != -1) { var decodeLen = i - _bufferDataIndex; string line = _encoding.GetString(_lineBuffer, _bufferDataIndex, decodeLen); _bufferDataIndex = i + _delimiterBytes.Length; _bufferDataLength -= decodeLen; _bufferDataLength -= _delimiterBytes.Length; var stop = _onLineRead(line, _state); if (stop) { OnFinish(); return; } } if (_bufferDataLength == _lineBuffer.Length) { OnException(new IndexOutOfRangeException("LineBuffer full! Try increace maxLineBytes!")); OnFinish(); return; } if (_bufferDataIndex > 0) { Buffer.BlockCopy(_lineBuffer, _bufferDataIndex, _lineBuffer, 0, _bufferDataLength); _bufferDataIndex = 0; } _socket.BeginReceive(_lineBuffer, _bufferDataLength, _lineBuffer.Length - _bufferDataLength, 0, ReceiveCallback, _bufferDataLength); } private void OnException(Exception ex) { _onException?.Invoke(ex, _state); } private void OnFinish() { _onFinish?.Invoke(_lineBuffer, _bufferDataIndex, _bufferDataLength, _state); } } }