/// <summary> /// Tries to read a buffer and parse out an IRC statement. /// </summary> /// <param name="buffer">The buffer to read from.</param> /// <param name="offset">The offset to begin reading. The parser may advance this, even if parsing fails.</param> /// <param name="count">The maximum number of bytes to read.</param> /// <param name="statement">The statement, if parsing succeeds, or <c>null</c>.</param> /// <returns><c>true</c> if parsing succeeded.</returns> public static bool TryParse(byte[] buffer, ref int offset, int count, out IrcStatement statement) { IrcStatementParseResult parseResult; return(TryParse(buffer, ref offset, count, out statement, out parseResult)); }
bool TryReceiveFromBuffer(out IrcStatement statement, out IrcStatementParseResult parseResult) { int offset = 0; bool gotStatement = IrcStatement.TryParse(_buffer, ref offset, _count, out statement, out parseResult); Debug.Assert(offset <= _count); if (offset != 0) { Array.Copy(_buffer, offset, _buffer, 0, _count - offset); _count -= offset; } return(gotStatement); }
internal static bool ValidateStatement(IrcStatement statement, out string errorMessage) { errorMessage = null; if (statement.Source != null && !ValidateIdentity(statement.Source, out errorMessage)) { return(false); } if (statement.Command == null) { errorMessage = "Command is not set."; return(false); } if (statement.Command.Length == 3 && statement.Command.All(x => x >= (byte)'0' && x <= (byte)'9')) { // Command is a number. } else if (statement.Command.Length > 0 && statement.Command.All(x => x >= (byte)'A' && x <= (byte)'Z')) { // Command is letters. } else { errorMessage = "Command is invalid."; return(false); } if (statement.Parameters.Count > IrcConstants.MaxParameters) { errorMessage = string.Format("IRC only allows up to {0} parameters.", IrcConstants.MaxParameters); return(false); } for (int i = 0; i < statement.Parameters.Count; i++) { var parameter = statement.Parameters[i]; if (parameter == null) { errorMessage = "No parameters may be null."; return(false); } if (!ValidateParameter(parameter, i == statement.Parameters.Count - 1, out errorMessage)) { return(false); } } return(true); }
/// <summary> /// Tries to receive an IRC statement. /// /// A blocking read is used. /// If you have a timeout set, <paramref name="parseResult"/> may be <see cref="IrcStatementParseResult.TimedOut"/>. /// </summary> /// <param name="statement">The statement.</param> /// <param name="parseResult">The parse result.</param> /// <returns><c>true</c> if a complete IRC statement was received.</returns> public bool TryReceive(out IrcStatement statement, out IrcStatementParseResult parseResult) { if (TryReceiveFromBuffer(out statement, out parseResult)) { return(true); } var receiveResult = TryReceiveFromStream(); if (receiveResult != IrcStatementParseResult.OK) { statement = null; parseResult = receiveResult; return(false); } return(TryReceiveFromBuffer(out statement, out parseResult)); }
/// <summary> /// Tries to read a buffer and parse out an IRC statement. /// Additionally, on failure, the reason for failure is returned. /// </summary> /// <param name="buffer">The buffer to read from.</param> /// <param name="offset">The offset to begin reading. The parser may advance this, even if parsing fails.</param> /// <param name="count">The maximum number of bytes to read.</param> /// <param name="statement">The statement, if parsing succeeds, or <c>null</c>.</param> /// <param name="parseResult">The result of parsing. On failure, this is the reason for the failure.</param> /// <returns><c>true</c> if parsing succeeded.</returns> public static bool TryParse(byte[] buffer, ref int offset, int count, out IrcStatement statement, out IrcStatementParseResult parseResult) { Throw.If.Null(buffer, "buffer"); string errorMessage = null; IrcString[] parts; statement = null; parseResult = IrcStatementParseResult.NothingToParse; // First, skip all initial CR/LF. SkipCrlf(buffer, ref offset, ref count); // See if we've got a CR or LF anywhere. int crlfIndex = IrcString.IndexOf(buffer, @byte => @byte == 13 || @byte == 10, offset, count); if (crlfIndex == -1) { if (count >= IrcConstants.MaxStatementLength) { parseResult = IrcStatementParseResult.StatementTooLong; } return(false); } // OK, let's get our string. var @string = new IrcString(buffer, offset, crlfIndex - offset); #if DEBUG var debugString = @string; #endif offset += @string.Length + 1; count -= @string.Length + 1; SkipCrlf(buffer, ref offset, ref count); if (crlfIndex >= IrcConstants.MaxStatementLength) { parseResult = IrcStatementParseResult.StatementTooLong; return(false); } // Do we have a prefix? statement = new IrcStatement(); if (@string.Length >= 1 && @string[0] == (byte)':') { parts = @string.Split((byte)' ', 2); var sourceString = parts[0].Substring(1); IrcIdentity source; if (!IrcIdentity.TryParse(sourceString, out source)) { goto invalid; } statement.Source = source; @string = parts.Length >= 2 ? parts[1] : IrcString.Empty; } // Now get the command. parts = @string.Split((byte)' ', 2); statement.Command = parts[0]; @string = parts.Length >= 2 ? parts[1] : IrcString.Empty; // Parameters, now... while (@string.Length > 0) { if (@string[0] == (byte)':') { statement.Parameters.Add(@string.Substring(1)); break; } else { parts = @string.Split((byte)' ', 2); statement.Parameters.Add(parts[0]); if (parts.Length == 1) { break; } @string = parts[1]; } } // We're done. If everything's kosher, we'll return true. if (!IrcValidation.ValidateStatement(statement, out errorMessage)) { goto invalid; } parseResult = IrcStatementParseResult.OK; return(true); invalid: #if DEBUG Console.WriteLine("Invalid statement '{0}' (error '{1}').", debugString, errorMessage); #endif statement = null; parseResult = IrcStatementParseResult.InvalidStatement; return(false); }