]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add power operator to expr
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 2 Dec 2013 18:39:38 +0000 (18:39 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 2 Dec 2013 20:34:47 +0000 (20:34 +0000)
Who needs left shift when you can do %{expr:(%{Acct-Output-Gigawords} * (2 ^ 32)) + %{Acct-Input-Octets}}}. Ok bad example, but i'm sure someone will find it useful.

src/include/libradius.h
src/lib/misc.c
src/modules/rlm_expr/rlm_expr.c

index 3f5819b2dc369f4eaa97d5bb8e58d0968632ca90..3be21d9c87932dc98f7e4240eaefa5c0b01d2cdb 100644 (file)
@@ -581,6 +581,7 @@ int fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, int port,
 int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
                       fr_ipaddr_t *ipaddr, int * port);
 ssize_t                fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen);
+int64_t                fr_pow(int32_t base, uint8_t exp);
 int            fr_get_time(char const *date_str, time_t *date);
 
 /*
index de8d13d9d5776d8ebc84519bb703d692ae92dcf2..d50cba5c45d35d73c56d8093c9a0ec87e2854a89 100644 (file)
@@ -814,6 +814,90 @@ ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inle
        return out - start;
 }
 
+/** Calculate powers
+ *
+ * @author Orson Peters
+ * @note Borrowed from the gist here: https://gist.github.com/nightcracker/3551590.
+ *
+ * @param base a 32bit signed integer.
+ * @param exp amount to raise base by.
+ * @return base ^ pow, or 0 on underflow/overflow.
+ */
+int64_t fr_pow(int32_t base, uint8_t exp) {
+       static const uint8_t highest_bit_set[] = {
+               0, 1, 2, 2, 3, 3, 3, 3,
+               4, 4, 4, 4, 4, 4, 4, 4,
+               5, 5, 5, 5, 5, 5, 5, 5,
+               5, 5, 5, 5, 5, 5, 5, 5,
+               6, 6, 6, 6, 6, 6, 6, 6,
+               6, 6, 6, 6, 6, 6, 6, 6,
+               6, 6, 6, 6, 6, 6, 6, 6,
+               6, 6, 6, 6, 6, 6, 6, 255, // anything past 63 is a guaranteed overflow with base > 1
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+               255, 255, 255, 255, 255, 255, 255, 255,
+       };
+
+       uint64_t result = 1;
+
+       switch (highest_bit_set[exp]) {
+       case 255: // we use 255 as an overflow marker and return 0 on overflow/underflow
+               if (base == 1) {
+                       return 1;
+               }
+
+               if (base == -1) {
+                       return 1 - 2 * (exp & 1);
+               }
+               return 0;
+       case 6:
+               if (exp & 1) result *= base;
+               exp >>= 1;
+               base *= base;
+       case 5:
+               if (exp & 1) result *= base;
+               exp >>= 1;
+               base *= base;
+       case 4:
+               if (exp & 1) result *= base;
+               exp >>= 1;
+               base *= base;
+       case 3:
+               if (exp & 1) result *= base;
+               exp >>= 1;
+               base *= base;
+       case 2:
+               if (exp & 1) result *= base;
+               exp >>= 1;
+               base *= base;
+       case 1:
+               if (exp & 1) result *= base;
+       default:
+               return result;
+       }
+}
+
 /*
  *     Sort of strtok/strsep function.
  */
index 5c55035bb78f009f4d6a8d94f07a34ef49bec016..be2b2d2fb5b4f93b1af8e2a2ad7b30644f19b7b8 100644 (file)
@@ -49,16 +49,17 @@ static const CONF_PARSER module_config[] = {
 };
 
 typedef enum expr_token_t {
-  TOKEN_NONE = 0,
-  TOKEN_INTEGER,
-  TOKEN_ADD,
-  TOKEN_SUBTRACT,
-  TOKEN_DIVIDE,
-  TOKEN_REMAINDER,
-  TOKEN_MULTIPLY,
-  TOKEN_AND,
-  TOKEN_OR,
-  TOKEN_LAST
+       TOKEN_NONE = 0,
+       TOKEN_INTEGER,
+       TOKEN_ADD,
+       TOKEN_SUBTRACT,
+       TOKEN_DIVIDE,
+       TOKEN_REMAINDER,
+       TOKEN_MULTIPLY,
+       TOKEN_AND,
+       TOKEN_OR,
+       TOKEN_POWER,
+       TOKEN_LAST
 } expr_token_t;
 
 typedef struct expr_map_t {
@@ -75,6 +76,7 @@ static expr_map_t map[] =
        {'%',   TOKEN_REMAINDER },
        {'&',   TOKEN_AND },
        {'|',   TOKEN_OR },
+       {'^',   TOKEN_POWER },
        {0,     TOKEN_LAST}
 };
 
@@ -226,6 +228,14 @@ static int get_number(REQUEST *request, char const **string, int64_t *answer)
                case TOKEN_OR:
                        result |= x;
                        break;
+
+               case TOKEN_POWER:
+                       if ((x > 255) || (x < 0)) {
+                               REDEBUG("Exponent must be between 0-255");
+                               return -1;
+                       }
+                       x = fr_pow((int32_t) result, (uint8_t) x);
+                       break;
                }
 
                /*