]> git.saurik.com Git - redis.git/blobdiff - src/util.c
Support dual encoding for ZRANGEBYSCORE et al
[redis.git] / src / util.c
index cc2794f6ab675ae4b88c6f9ae1d2782e802dc603..5cffa072ebf87392ea9baf34e45e782d66eaae77 100644 (file)
@@ -1,6 +1,7 @@
 #include "redis.h"
 #include <ctype.h>
 #include <limits.h>
+#include <math.h>
 
 /* Glob-style pattern matching. */
 int stringmatchlen(const char *pattern, int patternLen,
@@ -200,24 +201,83 @@ int ll2string(char *s, size_t len, long long value) {
     return l;
 }
 
-/* Check if the nul-terminated string 's' can be represented by a long
+/* Convert a double to a string representation. Returns the number of bytes
+ * required. The representation should always be parsable by stdtod(3). */
+int d2string(char *buf, size_t len, double value) {
+    if (isnan(value)) {
+        len = snprintf(buf,len,"nan");
+    } else if (isinf(value)) {
+        if (value < 0)
+            len = snprintf(buf,len,"-inf");
+        else
+            len = snprintf(buf,len,"inf");
+    } else if (value == 0) {
+        /* See: http://en.wikipedia.org/wiki/Signed_zero, "Comparisons". */
+        if (1.0/value < 0)
+            len = snprintf(buf,len,"-0");
+        else
+            len = snprintf(buf,len,"0");
+    } else {
+#if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0x7fffffffffffffffLL)
+        /* Check if the float is in a safe range to be casted into a
+         * long long. We are assuming that long long is 64 bit here.
+         * Also we are assuming that there are no implementations around where
+         * double has precision < 52 bit.
+         *
+         * Under this assumptions we test if a double is inside an interval
+         * where casting to long long is safe. Then using two castings we
+         * make sure the decimal part is zero. If all this is true we use
+         * integer printing function that is much faster. */
+        double min = -4503599627370495; /* (2^52)-1 */
+        double max = 4503599627370496; /* -(2^52) */
+        if (val > min && val < max && value == ((double)((long long)value)))
+            len = ll2string(buf,len,(long long)value);
+        else
+#endif
+            len = snprintf(buf,len,"%.17g",value);
+    }
+
+    return len;
+}
+
+/* Check if the sds string 's' can be represented by a long long
  * (that is, is a number that fits into long without any other space or
- * character before or after the digits).
+ * character before or after the digits, so that converting this number
+ * back to a string will result in the same bytes as the original string).
  *
- * If so, the function returns REDIS_OK and *longval is set to the value
+ * If so, the function returns REDIS_OK and *llongval is set to the value
  * of the number. Otherwise REDIS_ERR is returned */
-int isStringRepresentableAsLong(sds s, long *longval) {
+int isStringRepresentableAsLongLong(sds s, long long *llongval) {
     char buf[32], *endptr;
-    long value;
+    long long value;
     int slen;
 
-    value = strtol(s, &endptr, 10);
+    value = strtoll(s, &endptr, 10);
     if (endptr[0] != '\0') return REDIS_ERR;
     slen = ll2string(buf,32,value);
 
     /* If the number converted back into a string is not identical
      * then it's not possible to encode the string as integer */
     if (sdslen(s) != (unsigned)slen || memcmp(buf,s,slen)) return REDIS_ERR;
-    if (longval) *longval = value;
+    if (llongval) *llongval = value;
     return REDIS_OK;
 }
+
+int isStringRepresentableAsLong(sds s, long *longval) {
+    long long ll;
+
+    if (isStringRepresentableAsLongLong(s,&ll) == REDIS_ERR) return REDIS_ERR;
+    if (ll < LONG_MIN || ll > LONG_MAX) return REDIS_ERR;
+    *longval = (long)ll;
+    return REDIS_OK;
+}
+
+int isObjectRepresentableAsLongLong(robj *o, long long *llongval) {
+    redisAssert(o->type == REDIS_STRING);
+    if (o->encoding == REDIS_ENCODING_INT) {
+        if (llongval) *llongval = (long) o->ptr;
+        return REDIS_OK;
+    } else {
+        return isStringRepresentableAsLongLong(o->ptr,llongval);
+    }
+}