#include <freeradius-devel/util/strerror.h>
#include "calc.h"
-#define swap(_a, _b) do { __typeof__ (a) _tmp = _a; _a = _b; _b = _tmp; } while (0)
+#define swap(_a, _b) do { __typeof__ (_a) _tmp = _a; _a = _b; _b = _tmp; } while (0)
#define OVERFLOW (-3)
#define INVALID (-2)
+/** Updates type (a,b) -> c
+ *
+ * Note that we MUST have a less than b here
+ */
+static const fr_type_t upcast[FR_TYPE_FLOAT64 + 1][FR_TYPE_FLOAT64 + 1] = {
+ /*
+ * Prefix + int --> ipaddr
+ */
+ [FR_TYPE_IPV4_PREFIX] = {
+ [FR_TYPE_UINT8] = FR_TYPE_IPV4_ADDR,
+ [FR_TYPE_UINT16] = FR_TYPE_IPV4_ADDR,
+ [FR_TYPE_UINT32] = FR_TYPE_IPV4_ADDR,
+ [FR_TYPE_UINT64] = FR_TYPE_IPV4_ADDR,
+ [FR_TYPE_SIZE] = FR_TYPE_IPV4_ADDR,
+
+ [FR_TYPE_INT8] = FR_TYPE_IPV4_ADDR,
+ [FR_TYPE_INT16] = FR_TYPE_IPV4_ADDR,
+ [FR_TYPE_INT32] = FR_TYPE_IPV4_ADDR,
+ [FR_TYPE_INT64] = FR_TYPE_IPV4_ADDR,
+
+ [FR_TYPE_FLOAT32] = FR_TYPE_IPV4_ADDR,
+ [FR_TYPE_FLOAT64] = FR_TYPE_IPV4_ADDR,
+ },
+
+ [FR_TYPE_IPV6_PREFIX] = {
+ [FR_TYPE_UINT8] = FR_TYPE_IPV6_ADDR,
+ [FR_TYPE_UINT16] = FR_TYPE_IPV6_ADDR,
+ [FR_TYPE_UINT32] = FR_TYPE_IPV6_ADDR,
+ [FR_TYPE_UINT64] = FR_TYPE_IPV6_ADDR,
+ [FR_TYPE_SIZE] = FR_TYPE_IPV6_ADDR,
+
+ [FR_TYPE_INT8] = FR_TYPE_IPV6_ADDR,
+ [FR_TYPE_INT16] = FR_TYPE_IPV6_ADDR,
+ [FR_TYPE_INT32] = FR_TYPE_IPV6_ADDR,
+ [FR_TYPE_INT64] = FR_TYPE_IPV6_ADDR,
+
+ [FR_TYPE_FLOAT32] = FR_TYPE_IPV6_ADDR,
+ [FR_TYPE_FLOAT64] = FR_TYPE_IPV6_ADDR,
+ },
+
+ /*
+ * Various ints get cast to the next highest size which
+ * can hold their values.
+ */
+ [FR_TYPE_UINT8] = {
+ [FR_TYPE_UINT16] = FR_TYPE_UINT16,
+ [FR_TYPE_UINT32] = FR_TYPE_UINT32,
+ [FR_TYPE_UINT64] = FR_TYPE_UINT64,
+ [FR_TYPE_SIZE] = FR_TYPE_SIZE,
+ [FR_TYPE_FLOAT32] = FR_TYPE_FLOAT32,
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+
+ [FR_TYPE_INT16] = FR_TYPE_INT16,
+ [FR_TYPE_INT32] = FR_TYPE_INT32,
+ [FR_TYPE_INT64] = FR_TYPE_INT64,
+
+ [FR_TYPE_TIME_DELTA] = FR_TYPE_TIME_DELTA,
+ [FR_TYPE_DATE] = FR_TYPE_DATE,
+ },
+
+ [FR_TYPE_UINT16] = {
+ [FR_TYPE_UINT32] = FR_TYPE_UINT32,
+ [FR_TYPE_UINT64] = FR_TYPE_UINT64,
+ [FR_TYPE_SIZE] = FR_TYPE_SIZE,
+ [FR_TYPE_FLOAT32] = FR_TYPE_FLOAT32,
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+
+ [FR_TYPE_INT32] = FR_TYPE_INT32,
+ [FR_TYPE_INT64] = FR_TYPE_INT64,
+
+ [FR_TYPE_TIME_DELTA] = FR_TYPE_TIME_DELTA,
+ [FR_TYPE_DATE] = FR_TYPE_DATE,
+ },
+
+ [FR_TYPE_UINT32] = {
+ [FR_TYPE_UINT64] = FR_TYPE_UINT64,
+ [FR_TYPE_SIZE] = FR_TYPE_SIZE,
+ [FR_TYPE_FLOAT32] = FR_TYPE_FLOAT32,
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+
+ [FR_TYPE_INT64] = FR_TYPE_INT64,
+
+ [FR_TYPE_TIME_DELTA] = FR_TYPE_TIME_DELTA,
+ [FR_TYPE_DATE] = FR_TYPE_DATE,
+ },
+
+ [FR_TYPE_UINT64] = {
+ [FR_TYPE_SIZE] = FR_TYPE_SIZE,
+ [FR_TYPE_FLOAT32] = FR_TYPE_FLOAT32,
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+
+ [FR_TYPE_TIME_DELTA] = FR_TYPE_TIME_DELTA,
+ [FR_TYPE_DATE] = FR_TYPE_DATE,
+ },
+
+ [FR_TYPE_SIZE] = {
+ [FR_TYPE_FLOAT32] = FR_TYPE_FLOAT32,
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+ },
+
+ [FR_TYPE_DATE] = {
+ [FR_TYPE_INT16] = FR_TYPE_DATE,
+ [FR_TYPE_INT32] = FR_TYPE_DATE,
+ [FR_TYPE_INT64] = FR_TYPE_DATE,
+ [FR_TYPE_TIME_DELTA] = FR_TYPE_DATE,
+
+ [FR_TYPE_FLOAT32] = FR_TYPE_DATE,
+ [FR_TYPE_FLOAT64] = FR_TYPE_DATE,
+ },
+
+ /*
+ * Signed ints
+ */
+ [FR_TYPE_INT8] = {
+ [FR_TYPE_INT16] = FR_TYPE_INT16,
+ [FR_TYPE_INT32] = FR_TYPE_INT32,
+ [FR_TYPE_INT64] = FR_TYPE_INT64,
+ [FR_TYPE_FLOAT32] = FR_TYPE_FLOAT32,
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+ },
+
+ [FR_TYPE_INT16] = {
+ [FR_TYPE_INT32] = FR_TYPE_INT32,
+ [FR_TYPE_INT64] = FR_TYPE_INT64,
+ [FR_TYPE_FLOAT32] = FR_TYPE_FLOAT32,
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+ },
+
+ [FR_TYPE_INT32] = {
+ [FR_TYPE_INT64] = FR_TYPE_INT64,
+ [FR_TYPE_FLOAT32] = FR_TYPE_FLOAT32,
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+ },
+
+ [FR_TYPE_INT64] = {
+ [FR_TYPE_TIME_DELTA] = FR_TYPE_TIME_DELTA,
+ [FR_TYPE_FLOAT32] = FR_TYPE_FLOAT32,
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+ },
+
+ [FR_TYPE_TIME_DELTA] = {
+ [FR_TYPE_FLOAT32] = FR_TYPE_TIME_DELTA,
+ [FR_TYPE_FLOAT64] = FR_TYPE_TIME_DELTA,
+ },
+
+ [FR_TYPE_FLOAT32] = {
+ [FR_TYPE_FLOAT64] = FR_TYPE_FLOAT64,
+ },
+
+};
+
+
static int calc_date(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b)
{
fr_value_box_t one, two;
a = &one;
}
- if (b->type != FR_TYPE_OCTETS) {
+ if (b->type != FR_TYPE_STRING) {
if (fr_value_box_cast(ctx, &two, FR_TYPE_STRING, dst->enumv, b) < 0) return -1;
b = &two;
}
return 0;
}
-static int cast_ipv4_addr(fr_value_box_t *out, fr_value_box_t const *in, fr_dict_attr_t const *enumv)
+static int cast_ipv4_addr(fr_value_box_t *out, fr_value_box_t const *in)
{
switch (in->type) {
default:
+ fr_strerror_printf("Cannot operate on ipaddr and %s",
+ fr_table_str_by_value(fr_value_box_type_table, in->type, "<INVALID>"));
return -1;
- case FR_TYPE_STRING:
- if (fr_value_box_from_str(NULL, out, FR_TYPE_IPV4_ADDR, enumv,
- in->vb_strvalue, in->vb_length,
- NULL, in->tainted) < 0) return -1;
- break;
-
case FR_TYPE_IPV6_ADDR:
if (fr_value_box_cast(NULL, out, FR_TYPE_IPV4_ADDR, NULL, in) < 0) return -1;
break;
if (fr_value_box_cast(NULL, out, FR_TYPE_IPV4_PREFIX, NULL, in) < 0) return -1;
break;
+ /*
+ * All of these get mashed to 32-bits. The cast
+ * operation will check bounds (both negative and
+ * positive) on the run-time values.
+ */
case FR_TYPE_BOOL:
+
+ case FR_TYPE_UINT8:
+ case FR_TYPE_UINT16:
+ case FR_TYPE_UINT32:
+ case FR_TYPE_UINT64:
+
+ case FR_TYPE_SIZE:
+
case FR_TYPE_INT8:
case FR_TYPE_INT16:
case FR_TYPE_INT32:
case FR_TYPE_INT64:
+
case FR_TYPE_FLOAT32:
case FR_TYPE_FLOAT64:
- case FR_TYPE_UINT8:
- case FR_TYPE_UINT16:
- case FR_TYPE_UINT32:
- case FR_TYPE_UINT64:
- case FR_TYPE_SIZE:
- fr_value_box_copy(NULL, out, in);
+ if (fr_value_box_cast(NULL, out, FR_TYPE_UINT32, NULL, in) < 0) return -1;
break;
}
return 0;
}
-static int calc_ipv4_addr(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *in1, fr_token_t op, fr_value_box_t const *in2)
+static int calc_ipv4_addr(UNUSED TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *in1, fr_token_t op, fr_value_box_t const *in2)
{
- uint32_t num;
fr_value_box_t one, two;
fr_value_box_t *a, *b;
fr_assert(dst->type == FR_TYPE_IPV4_ADDR);
- if (cast_ipv4_addr(&one, in1, dst->enumv) < 0) return -1;
+ if (cast_ipv4_addr(&one, in1) < 0) return -1;
a = &one;
- if (cast_ipv4_addr(&two, in2, dst->enumv) < 0) return -1;
+ if (cast_ipv4_addr(&two, in2) < 0) return -1;
b = &two;
switch (op) {
/*
* We can only add something to a prefix, and
- * that something has to be a number.
+ * that something has to be a number. The cast
+ * operation already ensured that the number is
+ * uint32, and is at least vaguely within the
+ * allowed range.
*/
if (a->type != FR_TYPE_IPV4_PREFIX) return INVALID;
- switch (b->type) {
- case FR_TYPE_INT8:
- case FR_TYPE_INT16:
- case FR_TYPE_INT64:
- case FR_TYPE_FLOAT32:
- case FR_TYPE_FLOAT64:
- if (fr_value_box_cast_in_place(ctx, b, FR_TYPE_INT32, NULL) < 0) return -1;
- FALL_THROUGH;
+ if (b->type != FR_TYPE_UINT32) return INVALID;
- case FR_TYPE_INT32:
- if (b->vb_int32 < 0) return OVERFLOW;
+ /*
+ * Trying to add a number outside of the given prefix. That's not allowed.
+ */
+ if (b->vb_uint32 >= (((uint32_t) 1) << a->vb_ip.prefix)) return OVERFLOW;
- num = b->vb_int32;
- break;
+ dst->vb_ip.addr.v4.s_addr = a->vb_ip.addr.v4.s_addr + b->vb_uint32;
+ dst->vb_ip.prefix = 0;
+ break;
- case FR_TYPE_BOOL:
- case FR_TYPE_UINT8:
- case FR_TYPE_UINT16:
- case FR_TYPE_UINT64:
- case FR_TYPE_SIZE:
- if (fr_value_box_cast_in_place(ctx, b, FR_TYPE_UINT32, NULL) < 0) return -1;
- FALL_THROUGH;
+ default:
+ return INVALID;
+ }
- case FR_TYPE_UINT32:
- num = b->vb_uint32;
- break;
+ return 0;
+}
- default:
- /*
- * Can't add an IP address to a prefix.
- */
- return INVALID;
- }
+static int cast_ipv6_addr(fr_value_box_t *out, fr_value_box_t const *in)
+{
+ switch (in->type) {
+ default:
+ fr_strerror_printf("Cannot operate on ipv6addr and %s",
+ fr_table_str_by_value(fr_value_box_type_table, in->type, "<INVALID>"));
+ return -1;
+
+ case FR_TYPE_IPV4_ADDR:
+ if (fr_value_box_cast(NULL, out, FR_TYPE_IPV6_ADDR, NULL, in) < 0) return -1;
+ break;
+
+ case FR_TYPE_IPV4_PREFIX:
+ if (fr_value_box_cast(NULL, out, FR_TYPE_IPV6_PREFIX, NULL, in) < 0) return -1;
+ break;
+
+ /*
+ * All of these get mashed to 64-bits. The cast
+ * operation will check bounds (both negative and
+ * positive) on the run-time values.
+ */
+ case FR_TYPE_BOOL:
+
+ case FR_TYPE_UINT8:
+ case FR_TYPE_UINT16:
+ case FR_TYPE_UINT32:
+ case FR_TYPE_UINT64:
+
+ case FR_TYPE_SIZE:
+
+ case FR_TYPE_INT8:
+ case FR_TYPE_INT16:
+ case FR_TYPE_INT32:
+ case FR_TYPE_INT64:
+
+ case FR_TYPE_FLOAT32:
+ case FR_TYPE_FLOAT64:
+ if (fr_value_box_cast(NULL, out, FR_TYPE_UINT64, NULL, in) < 0) return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static int calc_ipv6_addr(UNUSED TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *in1, fr_token_t op, fr_value_box_t const *in2)
+{
+ fr_value_box_t one, two;
+ fr_value_box_t *a, *b;
+
+ fr_assert(dst->type == FR_TYPE_IPV6_ADDR);
+
+ if (cast_ipv6_addr(&one, in1) < 0) return -1;
+ a = &one;
+
+ if (cast_ipv6_addr(&two, in2) < 0) return -1;
+ b = &two;
+
+ switch (op) {
+ case T_OP_ADD:
+ /*
+ * For simplicity, make sure that the prefix is first.
+ */
+ if (b->type == FR_TYPE_IPV6_PREFIX) swap(a,b);
+
+ /*
+ * We can only add something to a prefix, and
+ * that something has to be a number. The cast
+ * operation already ensured that the number is
+ * uint32, and is at least vaguely within the
+ * allowed range.
+ */
+ if (a->type != FR_TYPE_IPV6_PREFIX) return INVALID;
+
+ if (b->type != FR_TYPE_UINT64) return INVALID;
/*
* Trying to add a number outside of the given prefix. That's not allowed.
*/
- if (num >= (((uint32_t) 1) << a->vb_ip.prefix)) return OVERFLOW;
+ if (b->vb_uint64 >= (((uint64_t) 1) << a->vb_ip.prefix)) return OVERFLOW;
- dst->vb_ip.addr.v4.s_addr = a->vb_ip.addr.v4.s_addr + num;
+#if 0
+ /*
+ * @todo - do the masking on stuff
+ */
+ dst->vb_ip.addr.v6.s_addr = a->vb_ip.addr.v6.s_addr + b->uint64;
+#endif
dst->vb_ip.prefix = 0;
+ dst->vb_ip.scope_id = a->vb_ip.scope_id;
break;
default:
return 0;
}
+static int calc_float32(UNUSED TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *in1, fr_token_t op, fr_value_box_t const *in2)
+{
+ fr_value_box_t one, two;
+ fr_value_box_t const *a = in1;
+ fr_value_box_t const *b = in2;
+
+ fr_assert(dst->type == FR_TYPE_FLOAT32);
+
+ /*
+ * Intermediate calculations are done using increased precision.
+ */
+ if (a->type != FR_TYPE_FLOAT64) {
+ if (fr_value_box_cast(ctx, &one, FR_TYPE_FLOAT64, NULL, a) < 0) return -1;
+ a = &one;
+ }
+
+ if (b->type != FR_TYPE_FLOAT64) {
+ if (fr_value_box_cast(ctx, &two, FR_TYPE_FLOAT64, NULL, b) < 0) return -1;
+ b = &two;
+ }
+
+ switch (op) {
+ case T_OP_ADD:
+ dst->vb_float32 = a->vb_float64 + b->vb_float64;
+ break;
+
+ case T_OP_SUB:
+ dst->vb_float32 = a->vb_float64 - b->vb_float64;
+ break;
+
+ default:
+ return INVALID;
+ }
+
+ return 0;
+
+}
+
+static int calc_float64(UNUSED TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *in1, fr_token_t op, fr_value_box_t const *in2)
+{
+ fr_value_box_t one, two;
+ fr_value_box_t const *a = in1;
+ fr_value_box_t const *b = in2;
+
+ fr_assert(dst->type == FR_TYPE_FLOAT64);
+
+ if (a->type != FR_TYPE_FLOAT64) {
+ if (fr_value_box_cast(ctx, &one, FR_TYPE_FLOAT64, NULL, a) < 0) return -1;
+ a = &one;
+ }
+
+ if (b->type != FR_TYPE_FLOAT64) {
+ if (fr_value_box_cast(ctx, &two, FR_TYPE_FLOAT64, NULL, b) < 0) return -1;
+ b = &two;
+ }
+
+ switch (op) {
+ case T_OP_ADD:
+ dst->vb_float64 = a->vb_float64 + b->vb_float64;
+ break;
+
+ case T_OP_SUB:
+ dst->vb_float64 = a->vb_float64 - b->vb_float64;
+ break;
+
+ default:
+ return INVALID;
+ }
+
+ return 0;
+
+}
+
+#define CALC(_t) static int calc_ ## _t(UNUSED TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *in1, fr_token_t op, fr_value_box_t const *in2) \
+{ \
+ switch (op) { \
+ case T_OP_ADD: \
+ if (!fr_add(&dst->vb_ ## _t, in1->vb_ ## _t, in2->vb_ ## _t)) return OVERFLOW; \
+ break; \
+ \
+ case T_OP_SUB: \
+ if (!fr_sub(&dst->vb_ ## _t, in1->vb_ ## _t, in2->vb_ ## _t)) return OVERFLOW; \
+ break; \
+ \
+ default: \
+ return INVALID; \
+ } \
+ \
+ return 0; \
+}
+
+CALC(uint8)
+CALC(uint16)
+CALC(uint32)
+CALC(uint64)
+
+CALC(int8)
+CALC(int16)
+CALC(int32)
+CALC(int64)
+
typedef int (*fr_binary_op_t)(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b);
-static const fr_binary_op_t calc_type[FR_TYPE_MAX] = {
- [FR_TYPE_OCTETS] = calc_octets,
- [FR_TYPE_STRING] = calc_string,
+static const fr_binary_op_t calc_integer_type[FR_TYPE_MAX + 1] = {
+ [FR_TYPE_UINT8] = calc_uint8,
+ [FR_TYPE_UINT16] = calc_uint16,
+ [FR_TYPE_UINT32] = calc_uint32,
+ [FR_TYPE_UINT64] = calc_uint64,
- [FR_TYPE_DATE] = calc_date,
- [FR_TYPE_TIME_DELTA] = calc_time_delta,
+ [FR_TYPE_INT8] = calc_int8,
+ [FR_TYPE_INT16] = calc_int16,
+ [FR_TYPE_INT32] = calc_int32,
+ [FR_TYPE_INT64] = calc_int64,
- [FR_TYPE_IPV4_ADDR] = calc_ipv4_addr,
};
-int fr_value_calc(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t hint, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b)
+static int calc_integer(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_value_box_t const *in1, fr_token_t op, fr_value_box_t const *in2)
{
int rcode;
+ fr_type_t type;
+ fr_value_box_t const *a = in1;
+ fr_value_box_t const *b = in2;
+ fr_value_box_t one, two, out;
+
+ /*
+ * All of the types are the same. Just do the work.
+ */
+ if ((dst->type == in1->type) &&
+ (dst->type == in2->type)) {
+ return calc_integer_type[dst->type](ctx, dst, in1, op, in2);
+ }
+
+ /*
+ * Upcast to the largest type which will handle the
+ * calculations.
+ */
+ type = dst->type;
+ if (upcast[type][a->type] != FR_TYPE_NULL) {
+ type = upcast[type][a->type];
+
+ } else if (upcast[a->type][type] != FR_TYPE_NULL) {
+ type = upcast[a->type][type];
+ }
- if (fr_type_is_structural(dst->type)) {
- fr_strerror_const("Cannot calculate results for structural types");
+ if (upcast[type][b->type] != FR_TYPE_NULL) {
+ type = upcast[type][b->type];
+
+ } else if (upcast[b->type][type] != FR_TYPE_NULL) {
+ type = upcast[b->type][type];
+ }
+
+ if (a->type != type) {
+ if (fr_value_box_cast(ctx, &one, type, NULL, a) < 0) return -1;
+ a = &one;
+ }
+
+ if (b->type != type) {
+ if (fr_value_box_cast(ctx, &two, type, NULL, b) < 0) return -1;
+ b = &two;
+ }
+
+ if (!calc_integer_type[type]) {
+ fr_strerror_const("Not yet implemented");
return -1;
}
+ fr_value_box_init(&out, type, dst->enumv, false);
+ rcode = calc_integer_type[type](ctx, &out, a, op, b);
+ if (rcode < 0) return rcode;
+
+ /*
+ * Then once we're done, cast the result to the final
+ * output type.
+ */
+ return fr_value_box_cast(ctx, dst, dst->type, dst->enumv, &out);
+}
+
+static const fr_binary_op_t calc_type[FR_TYPE_MAX] = {
+ [FR_TYPE_OCTETS] = calc_octets,
+ [FR_TYPE_STRING] = calc_string,
+
+ [FR_TYPE_IPV4_ADDR] = calc_ipv4_addr,
+ [FR_TYPE_IPV6_ADDR] = calc_ipv6_addr,
+
+ [FR_TYPE_UINT8] = calc_integer,
+ [FR_TYPE_UINT16] = calc_integer,
+ [FR_TYPE_UINT32] = calc_integer,
+ [FR_TYPE_UINT64] = calc_integer,
+
+ [FR_TYPE_SIZE] = calc_integer,
+
+ [FR_TYPE_INT8] = calc_integer,
+ [FR_TYPE_INT16] = calc_integer,
+ [FR_TYPE_INT32] = calc_integer,
+ [FR_TYPE_INT64] = calc_integer,
+
+ [FR_TYPE_DATE] = calc_date,
+ [FR_TYPE_TIME_DELTA] = calc_time_delta,
+
+ [FR_TYPE_FLOAT32] = calc_float32,
+ [FR_TYPE_FLOAT64] = calc_float64,
+};
+
+int fr_value_calc(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t hint, fr_value_box_t const *a, fr_token_t op, fr_value_box_t const *b)
+{
+ int rcode = -1;
+ fr_value_box_t one, two;
+
+ fr_assert(fr_type_is_leaf(a->type));
+ fr_assert(fr_type_is_leaf(b->type));
+ fr_assert((hint == FR_TYPE_NULL) || fr_type_is_leaf(hint));
+
+ fr_value_box_init_null(&one);
+ fr_value_box_init_null(&two);
+
+ /*
+ * We don't know what the output type should be. Try to
+ * guess based on a variety of factors.
+ */
+ if (hint == FR_TYPE_NULL) do {
+ fr_type_t a_type, b_type;
+
+ switch (op) {
+ case T_OP_PREPEND:
+ /*
+ * Pick the existing type if we have a
+ * variable-sized type. Otherwise, pick
+ * octets.
+ */
+ if (fr_type_is_variable_size(a->type)) {
+ hint = a->type;
+
+ } else if (fr_type_is_variable_size(b->type)) {
+ hint = b->type;
+
+ } else {
+ hint = FR_TYPE_OCTETS;
+ }
+ break;
+
+ case T_OP_CMP_EQ:
+ case T_OP_NE:
+ case T_OP_GE:
+ case T_OP_GT:
+ case T_OP_LE:
+ case T_OP_LT:
+ /*
+ * Comparison operators always return
+ * "bool".
+ */
+ hint = FR_TYPE_BOOL;
+ break;
+
+ case T_OP_ADD:
+ case T_OP_SUB:
+ if (a->type == b->type) {
+ hint = a->type;
+ break;
+ }
+
+ /*
+ * Non-comparison operators: Strings of different types always
+ * results in octets.
+ */
+ if (fr_type_is_variable_size(a->type) && fr_type_is_variable_size(b->type)) {
+ hint = FR_TYPE_OCTETS;
+ break;
+ }
+
+ /*
+ * Nothing else set it. If the input types are
+ * the same, then that must be the output type.
+ */
+ if (a->type == b->type) {
+ hint = a->type;
+ break;
+ }
+
+ /*
+ * Try to "up-cast" the types. This is
+ * so that we can take (for example)
+ * uint8 + uint16, and have the output as
+ * uint16.
+ */
+ a_type = a->type;
+ b_type = b->type;
+ if (a_type > b_type) swap(a_type, b_type);
+
+ hint = upcast[a_type][b_type];
+ if (hint != FR_TYPE_NULL) {
+ break;
+ }
+
+ /*
+ * No idea what to do. :(
+ */
+ fr_strerror_const("Unable to automatically determine output data type");
+ goto done;
+
+ default:
+ return INVALID;
+ }
+ } while (0);
+
+ /*
+ * If we're doing operations between
+ * STRING/OCTETS and another type, then cast the
+ * variable sized type to the fixed size type.
+ * Doing this casting here makes the rest of the
+ * code simpler.
+ *
+ * This isn't always the best thing to do, but it makes
+ * sense in most situations. It allows comparisons,
+ * etc. to operate between strings and integers.
+ */
+ if (!fr_type_is_variable_size(hint)) {
+ if (fr_type_is_variable_size(a->type) && !fr_type_is_variable_size(b->type)) {
+ if (fr_value_box_cast(NULL, &one, b->type, b->enumv, a) < 0) goto done;
+ a = &one;
+
+ } else if (!fr_type_is_variable_size(a->type) && fr_type_is_variable_size(b->type)) {
+ if (fr_value_box_cast(NULL, &two, a->type, a->enumv, b) < 0) goto done;
+ b = &two;
+ }
+ }
+
switch (op) {
case T_OP_CMP_EQ:
case T_OP_NE:
case T_OP_GT:
case T_OP_LE:
case T_OP_LT:
- if ((hint != FR_TYPE_NULL) && (hint != FR_TYPE_BOOL)) {
- fr_strerror_const("Invalid destination type for comparison operator");
- return -1;
+ if (hint != FR_TYPE_BOOL) {
+ fr_strerror_printf("Invalid destination type '%s' for comparison operator",
+ fr_table_str_by_value(fr_value_box_type_table, dst->type, "<INVALID>"));
+ goto done;
}
fr_value_box_clear_value(dst);
rcode = fr_value_box_cmp_op(op, a, b);
if (rcode < 0) {
- fr_strerror_const("Failed doing comparison");
- return -1;
+ goto done;
}
dst->vb_bool = (rcode > 0);
- return 0;
+ rcode = 0;
+ break;
case T_OP_ADD:
case T_OP_SUB:
if (hint == FR_TYPE_NULL) {
if (a->type != b->type) {
fr_strerror_const("not yet implemented");
- return -1;
+ goto done;
}
fr_value_box_init(dst, hint, NULL, false);
}
if (!calc_type[dst->type]) {
- fr_strerror_printf("No handler has been implemented for leaf type %s",
+ fr_strerror_printf("Cannot perform any operations for destination type %s",
fr_table_str_by_value(fr_value_box_type_table, dst->type, "<INVALID>"));
rcode = -1;
break;
fr_table_str_by_value(fr_value_box_type_table, dst->type, "<INVALID>"));
}
+done:
+ if (rcode == 0) dst->tainted = a->tainted | b->tainted;
+
+ fr_value_box_clear(&one);
+ fr_value_box_clear(&two);
+
return rcode;
}