MEDIUM: samples: provide basic arithmetic and bitwise operators

This commit introduces a new category of converters. They are bitwise and
arithmetic operators which support performing basic operations on integers.
Some bitwise operations are supported (and, or, xor, cpl) and some arithmetic
operations are supported (add, sub, mul, div, mod, neg). Some comparators
are provided (odd, even, not, bool) which make it possible to report a match
without having to write an ACL.

The detailed list of new operators as they appear in the doc is :

add(<value>)
  Adds <value> to the input value of type unsigned integer, and returns the
  result as an unsigned integer.

and(<value>)
  Performs a bitwise "AND" between <value> and the input value of type unsigned
  integer, and returns the result as an unsigned integer.

bool
  Returns a boolean TRUE if the input value of type unsigned integer is
  non-null, otherwise returns FALSE. Used in conjunction with and(), it can be
  used to report true/false for bit testing on input values (eg: verify the
  presence of a flag).

cpl
  Takes the input value of type unsigned integer, applies a twos-complement
  (flips all bits) and returns the result as an unsigned integer.

div(<value>)
  Divides the input value of type unsigned integer by <value>, and returns the
  result as an unsigned integer. If <value> is null, the largest unsigned
  integer is returned (typically 2^32-1).

even
  Returns a boolean TRUE if the input value of type unsigned integer is even
  otherwise returns FALSE. It is functionally equivalent to "not,and(1),bool".

mod(<value>)
  Divides the input value of type unsigned integer by <value>, and returns the
  remainder as an unsigned integer. If <value> is null, then zero is returned.

mul(<value>)
  Multiplies the input value of type unsigned integer by <value>, and returns
  the product as an unsigned integer. In case of overflow, the higher bits are
  lost, leading to seemingly strange values.

neg
  Takes the input value of type unsigned integer, computes the opposite value,
  and returns the remainder as an unsigned integer. 0 is identity. This
  operator is provided for reversed subtracts : in order to subtract the input
  from a constant, simply perform a "neg,add(value)".

not
  Returns a boolean FALSE if the input value of type unsigned integer is
  non-null, otherwise returns TRUE. Used in conjunction with and(), it can be
  used to report true/false for bit testing on input values (eg: verify the
  absence of a flag).

odd
  Returns a boolean TRUE if the input value of type unsigned integer is odd
  otherwise returns FALSE. It is functionally equivalent to "and(1),bool".

or(<value>)
  Performs a bitwise "OR" between <value> and the input value of type unsigned
  integer, and returns the result as an unsigned integer.

sub(<value>)
  Subtracts <value> from the input value of type unsigned integer, and returns
  the result as an unsigned integer. Note: in order to subtract the input from
  a constant, simply perform a "neg,add(value)".

xor(<value>)
  Performs a bitwise "XOR" (exclusive OR) between <value> and the input value
  of type unsigned integer, and returns the result as an unsigned integer.
diff --git a/src/sample.c b/src/sample.c
index a1c6b46..57b2c00 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -1888,6 +1888,144 @@
 	return 1;
 }
 
+/* Takes a UINT on input, applies a binary twos complement and returns the UINT
+ * result.
+ */
+static int sample_conv_binary_cpl(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint = ~smp->data.uint;
+	return 1;
+}
+
+/* Takes a UINT on input, applies a binary "and" with the UINT in arg_p, and
+ * returns the UINT result.
+ */
+static int sample_conv_binary_and(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint &= arg_p->data.uint;
+	return 1;
+}
+
+/* Takes a UINT on input, applies a binary "or" with the UINT in arg_p, and
+ * returns the UINT result.
+ */
+static int sample_conv_binary_or(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint |= arg_p->data.uint;
+	return 1;
+}
+
+/* Takes a UINT on input, applies a binary "xor" with the UINT in arg_p, and
+ * returns the UINT result.
+ */
+static int sample_conv_binary_xor(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint ^= arg_p->data.uint;
+	return 1;
+}
+
+/* Takes a UINT on input, applies an arithmetic "add" with the UINT in arg_p,
+ * and returns the UINT result.
+ */
+static int sample_conv_arith_add(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint += arg_p->data.uint;
+	return 1;
+}
+
+/* Takes a UINT on input, applies an arithmetic "sub" with the UINT in arg_p,
+ * and returns the UINT result.
+ */
+static int sample_conv_arith_sub(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint -= arg_p->data.uint;
+	return 1;
+}
+
+/* Takes a UINT on input, applies an arithmetic "mul" with the UINT in arg_p,
+ * and returns the UINT result.
+ */
+static int sample_conv_arith_mul(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint *= arg_p->data.uint;
+	return 1;
+}
+
+/* Takes a UINT on input, applies an arithmetic "div" with the UINT in arg_p,
+ * and returns the UINT result. If arg_p makes the result overflow, then the
+ * largest possible quantity is returned.
+ */
+static int sample_conv_arith_div(const struct arg *arg_p, struct sample *smp)
+{
+	if (arg_p->data.uint)
+		smp->data.uint /= arg_p->data.uint;
+	else
+		smp->data.uint = ~0;
+	return 1;
+}
+
+/* Takes a UINT on input, applies an arithmetic "mod" with the UINT in arg_p,
+ * and returns the UINT result. If arg_p makes the result overflow, then zero
+ * is returned.
+ */
+static int sample_conv_arith_mod(const struct arg *arg_p, struct sample *smp)
+{
+	if (arg_p->data.uint)
+		smp->data.uint %= arg_p->data.uint;
+	else
+		smp->data.uint = 0;
+	return 1;
+}
+
+/* Takes an UINT on input, applies an arithmetic "neg" and returns the UINT
+ * result.
+ */
+static int sample_conv_arith_neg(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint = -smp->data.uint;
+	return 1;
+}
+
+/* Takes a UINT on input, returns true is the value is non-null, otherwise
+ * false. The output is a BOOL.
+ */
+static int sample_conv_arith_bool(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint = !!smp->data.uint;
+	smp->type = SMP_T_BOOL;
+	return 1;
+}
+
+/* Takes a UINT on input, returns false is the value is non-null, otherwise
+ * truee. The output is a BOOL.
+ */
+static int sample_conv_arith_not(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint = !smp->data.uint;
+	smp->type = SMP_T_BOOL;
+	return 1;
+}
+
+/* Takes a UINT on input, returns true is the value is odd, otherwise false.
+ * The output is a BOOL.
+ */
+static int sample_conv_arith_odd(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint = smp->data.uint & 1;
+	smp->type = SMP_T_BOOL;
+	return 1;
+}
+
+/* Takes a UINT on input, returns true is the value is even, otherwise false.
+ * The output is a BOOL.
+ */
+static int sample_conv_arith_even(const struct arg *arg_p, struct sample *smp)
+{
+	smp->data.uint = !(smp->data.uint & 1);
+	smp->type = SMP_T_BOOL;
+	return 1;
+}
+
 /************************************************************************/
 /*       All supported sample fetch functions must be declared here     */
 /************************************************************************/
@@ -2034,6 +2172,22 @@
 	{ "field",  sample_conv_field,     ARG2(2,UINT,STR), sample_conv_field_check, SMP_T_STR,  SMP_T_STR },
 	{ "word",   sample_conv_word,      ARG2(2,UINT,STR), sample_conv_field_check, SMP_T_STR,  SMP_T_STR },
 	{ "regsub", sample_conv_regsub,    ARG3(2,REG,STR,STR), sample_conv_regsub_check, SMP_T_STR, SMP_T_STR },
+
+	{ "and",    sample_conv_binary_and, ARG1(1,UINT), NULL, SMP_T_UINT, SMP_T_UINT },
+	{ "or",     sample_conv_binary_or,  ARG1(1,UINT), NULL, SMP_T_UINT, SMP_T_UINT },
+	{ "xor",    sample_conv_binary_xor, ARG1(1,UINT), NULL, SMP_T_UINT, SMP_T_UINT },
+	{ "cpl",    sample_conv_binary_cpl,            0, NULL, SMP_T_UINT, SMP_T_UINT },
+	{ "bool",   sample_conv_arith_bool,            0, NULL, SMP_T_UINT, SMP_T_BOOL },
+	{ "not",    sample_conv_arith_not,             0, NULL, SMP_T_UINT, SMP_T_BOOL },
+	{ "odd",    sample_conv_arith_odd,             0, NULL, SMP_T_UINT, SMP_T_BOOL },
+	{ "even",   sample_conv_arith_even,            0, NULL, SMP_T_UINT, SMP_T_BOOL },
+	{ "add",    sample_conv_arith_add,  ARG1(1,UINT), NULL, SMP_T_UINT, SMP_T_UINT },
+	{ "sub",    sample_conv_arith_sub,  ARG1(1,UINT), NULL, SMP_T_UINT, SMP_T_UINT },
+	{ "mul",    sample_conv_arith_mul,  ARG1(1,UINT), NULL, SMP_T_UINT, SMP_T_UINT },
+	{ "div",    sample_conv_arith_div,  ARG1(1,UINT), NULL, SMP_T_UINT, SMP_T_UINT },
+	{ "mod",    sample_conv_arith_mod,  ARG1(1,UINT), NULL, SMP_T_UINT, SMP_T_UINT },
+	{ "neg",    sample_conv_arith_neg,             0, NULL, SMP_T_UINT, SMP_T_UINT },
+
 	{ NULL, NULL, 0, 0, 0 },
 }};