/// <summary> /// Converts the IRC statement into an IRC string containing all of its bytes, /// including the ending CR+LF, and additionally returns whether the string was truncated. /// </summary> /// <param name="truncated"><c>true</c> if the string was too long and had to be truncated.</param> /// <returns>An IRC string.</returns> public IrcString ToIrcString(out bool truncated) { string errorMessage; if (!IrcValidation.ValidateStatement(this, out errorMessage)) { throw new InvalidOperationException(errorMessage); } // Convert to an IrcString first. truncated = false; var @string = IrcString.Empty; if (Source != null) { @string += ":" + Source.ToIrcString() + " "; } @string += Command; for (int i = 0; i < Parameters.Count; i++) { var parameter = Parameters[i]; @string += " "; if (i == Parameters.Count - 1 && parameter.Contains((byte)' ')) { @string += ":"; } @string += parameter; } // Now convert to a byte array, truncate if need be, and add CR+LF. if (@string.Length > IrcConstants.MaxStatementLength - 2) { @string = @string.Substring(0, IrcConstants.MaxStatementLength - 2); truncated = true; } @string += "\r\n"; return(@string); }
/// <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); }