/// <summary> /// Applies the specified value. /// </summary> /// <param name="value">The value.</param> private void applyString(byte[] value) { if (operations.Count == 0) { receivedData.Enqueue(value); return; } object nextOp = operations.Peek(); if (nextOp is AwaitString && !((AwaitString)nextOp).readSize) { AwaitString op = (AwaitString)nextOp; int size = Convert.ToInt32(Encoding.ASCII.GetString(value)); op.readSize = true; op.size = size; op.data = new byte[] {}; if (op.size < 0) { operations.Pop(); applyString(null); } } else if (nextOp is AwaitList && !((AwaitList)nextOp).readLength) { AwaitList op = (AwaitList)nextOp; int length = Convert.ToInt32(Encoding.ASCII.GetString(value)); op.readLength = true; op.length = length; if (op.length < 0) { operations.Pop(); applyString(null); } op.data = new object[length]; } else { if (!applyToList(value)) { receivedData.Enqueue(value); operations.Push(new AwaitCrLf()); } } }
/// <summary> /// Updates the internal states based on the latest bytes read. /// </summary> /// <param name="bytesRead">The number of bytes read.</param> /// <exception cref="System.NotImplementedException"> /// Operation not implemented for + encoding.GetString(buffer, bufferIndex, 1) /// or /// Don't know what to do. /// </exception> /// <exception cref="System.InvalidOperationException"> /// Protocol Violation. Expected CR-LF. /// or /// Protocol Violation. Expected CR. /// or /// Protocol Violation. Expected LF. /// </exception> public void update(int bytesRead) { int bufferIndex = 0; while (bufferIndex < buffer.Length && bufferIndex < bytesRead) { if (operations.Count == 0) { operations.Push(new AwaitCommand()); } object nextOp = operations.Peek(); if (nextOp is AwaitCommand) { switch (buffer[bufferIndex]) { case PLUS: operations.Pop(); operations.Push(new AwaitSimpleString()); ++bufferIndex; continue; case DOLLAR: operations.Pop(); operations.Push(new AwaitString { readSize = false }); operations.Push(new AwaitSimpleString()); ++bufferIndex; continue; case COLON: operations.Pop(); operations.Push(new AwaitInt()); ++bufferIndex; continue; case ASTERISK: operations.Pop(); operations.Push(new AwaitList { readLength = false }); operations.Push(new AwaitSimpleString()); ++bufferIndex; continue; case MINUS: operations.Pop(); operations.Push(new AwaitError()); operations.Push(new AwaitSimpleString()); ++bufferIndex; continue; default: throw new NotImplementedException("Operation not implemented for " + encoding.GetString(buffer, bufferIndex, 1)); } } else if (nextOp is AwaitCrLf) { AwaitCrLf op = (AwaitCrLf)nextOp; int availableLength = buffer.Length - bufferIndex; if (availableLength < 1) { continue; } if (!op.cr) { if (availableLength >= 2) { if (buffer[bufferIndex] == CR && buffer[bufferIndex + 1] == LF) { operations.Pop(); bufferIndex += 2; } else { throw new InvalidOperationException("Protocol Violation. Expected CR-LF."); } } else { if (buffer[bufferIndex] == CR) { op.cr = true; ++bufferIndex; } else { throw new InvalidOperationException("Protocol Violation. Expected CR."); } } } else { if (buffer[bufferIndex] == LF) { operations.Pop(); ++bufferIndex; } else { throw new InvalidOperationException("Protocol Violation. Expected LF."); } } } else if (nextOp is AwaitSimpleString) { AwaitSimpleString op = (AwaitSimpleString)nextOp; IEnumerable <byte> partialBuffer = buffer.Skip(bufferIndex).Take(bytesRead - bufferIndex); byte[] potential = op.data == null?partialBuffer.ToArray() : op.data.Concat(partialBuffer).ToArray(); int indexCrLf = potential.indexOf(CrLf); if (indexCrLf == -1) { op.data = potential; bufferIndex = buffer.Length; } else { byte[] stringData = potential.Take(indexCrLf).ToArray(); operations.Pop(); if (operations.Count > 0 && operations.Peek() is AwaitError) { operations.Pop(); apply(new RedisErrorException(encoding.GetString(stringData))); } else { applyString(stringData); } bufferIndex += indexCrLf + CrLf.Length - (op.data == null ? 0 : op.data.Length); } } else if (nextOp is AwaitInt) { AwaitInt op = (AwaitInt)nextOp; IEnumerable <byte> partialBuffer = buffer.Skip(bufferIndex).Take(bytesRead - bufferIndex); byte[] potential = op.data == null?partialBuffer.ToArray() : op.data.Concat(partialBuffer).ToArray(); int indexCrLf = potential.indexOf(CrLf); if (indexCrLf == -1) { op.data = potential; bufferIndex = buffer.Length; } else { byte[] stringData = potential.Take(indexCrLf).ToArray(); string valueLiteral = _encoding.GetString(stringData); operations.Pop(); long value; if (long.TryParse(valueLiteral, out value)) { apply(value); } else { apply(new Exception(string.Format("Failed to parse {0} as an integer", valueLiteral))); } bufferIndex += indexCrLf + CrLf.Length - (op.data == null ? 0 : op.data.Length); } } else if (nextOp is AwaitString) { AwaitString op = (AwaitString)nextOp; int currentSize = op.data.Length; int availableLength = buffer.Length - bufferIndex; if (currentSize + availableLength >= op.size) { int count = op.size - op.data.Length; byte[] stringData = op.data.Concat(buffer.Skip(bufferIndex).Take(count)).ToArray(); operations.Pop(); applyString(stringData); bufferIndex = bufferIndex + count; operations.Push(new AwaitCrLf()); continue; } else { op.data = op.data.Concat(buffer.Skip(bufferIndex).Take(availableLength)).ToArray(); bufferIndex = bufferIndex + availableLength; } } else if (nextOp is AwaitList) { AwaitList op = (AwaitList)nextOp; if (op.objectsRead < op.length) { operations.Push(new AwaitCommand()); } } else { throw new NotImplementedException("Don't know what to do."); } nextOp = operations.Count > 0 ? operations.Peek() : null; while (nextOp is AwaitList && ((AwaitList)nextOp).objectsRead >= ((AwaitList)nextOp).length) { operations.Pop(); apply(((AwaitList)nextOp).data); nextOp = operations.Count > 0 ? operations.Peek() : null; } if (operations.Count == 0) { // Opportunity here to convert from "internal" state to "external" state. Queue receivedObject = new Queue(receivedData.Count); while (receivedData.Count > 0) { receivedObject.Enqueue(receivedData.Dequeue()); } _onObjectReceived(new ObjectReceivedEventArgs(receivedObject)); } } }