kstrto*: converting strings to integers done (hopefully) right
[firefly-linux-kernel-4.4.55.git] / lib / kstrtox.c
diff --git a/lib/kstrtox.c b/lib/kstrtox.c
new file mode 100644 (file)
index 0000000..05672e8
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Convert integer string representation to an integer.
+ * If an integer doesn't fit into specified type, -E is returned.
+ *
+ * Integer starts with optional sign.
+ * kstrtou*() functions do not accept sign "-".
+ *
+ * Radix 0 means autodetection: leading "0x" implies radix 16,
+ * leading "0" implies radix 8, otherwise radix is 10.
+ * Autodetection hints work after optional sign, but not before.
+ *
+ * If -E is returned, result is not touched.
+ */
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+static inline char _tolower(const char c)
+{
+       return c | 0x20;
+}
+
+static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
+{
+       unsigned long long acc;
+       int ok;
+
+       if (base == 0) {
+               if (s[0] == '0') {
+                       if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
+                               base = 16;
+                       else
+                               base = 8;
+               } else
+                       base = 10;
+       }
+       if (base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
+               s += 2;
+
+       acc = 0;
+       ok = 0;
+       while (*s) {
+               unsigned int val;
+
+               if ('0' <= *s && *s <= '9')
+                       val = *s - '0';
+               else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
+                       val = _tolower(*s) - 'a' + 10;
+               else if (*s == '\n') {
+                       if (*(s + 1) == '\0')
+                               break;
+                       else
+                               return -EINVAL;
+               } else
+                       return -EINVAL;
+
+               if (val >= base)
+                       return -EINVAL;
+               if (acc > div_u64(ULLONG_MAX - val, base))
+                       return -ERANGE;
+               acc = acc * base + val;
+               ok = 1;
+
+               s++;
+       }
+       if (!ok)
+               return -EINVAL;
+       *res = acc;
+       return 0;
+}
+
+int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
+{
+       if (s[0] == '+')
+               s++;
+       return _kstrtoull(s, base, res);
+}
+EXPORT_SYMBOL(kstrtoull);
+
+int kstrtoll(const char *s, unsigned int base, long long *res)
+{
+       unsigned long long tmp;
+       int rv;
+
+       if (s[0] == '-') {
+               rv = _kstrtoull(s + 1, base, &tmp);
+               if (rv < 0)
+                       return rv;
+               if ((long long)(-tmp) >= 0)
+                       return -ERANGE;
+               *res = -tmp;
+       } else {
+               rv = kstrtoull(s, base, &tmp);
+               if (rv < 0)
+                       return rv;
+               if ((long long)tmp < 0)
+                       return -ERANGE;
+               *res = tmp;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(kstrtoll);
+
+/* Internal, do not use. */
+int _kstrtoul(const char *s, unsigned int base, unsigned long *res)
+{
+       unsigned long long tmp;
+       int rv;
+
+       rv = kstrtoull(s, base, &tmp);
+       if (rv < 0)
+               return rv;
+       if (tmp != (unsigned long long)(unsigned long)tmp)
+               return -ERANGE;
+       *res = tmp;
+       return 0;
+}
+EXPORT_SYMBOL(_kstrtoul);
+
+/* Internal, do not use. */
+int _kstrtol(const char *s, unsigned int base, long *res)
+{
+       long long tmp;
+       int rv;
+
+       rv = kstrtoll(s, base, &tmp);
+       if (rv < 0)
+               return rv;
+       if (tmp != (long long)(long)tmp)
+               return -ERANGE;
+       *res = tmp;
+       return 0;
+}
+EXPORT_SYMBOL(_kstrtol);
+
+int kstrtouint(const char *s, unsigned int base, unsigned int *res)
+{
+       unsigned long long tmp;
+       int rv;
+
+       rv = kstrtoull(s, base, &tmp);
+       if (rv < 0)
+               return rv;
+       if (tmp != (unsigned long long)(unsigned int)tmp)
+               return -ERANGE;
+       *res = tmp;
+       return 0;
+}
+EXPORT_SYMBOL(kstrtouint);
+
+int kstrtoint(const char *s, unsigned int base, int *res)
+{
+       long long tmp;
+       int rv;
+
+       rv = kstrtoll(s, base, &tmp);
+       if (rv < 0)
+               return rv;
+       if (tmp != (long long)(int)tmp)
+               return -ERANGE;
+       *res = tmp;
+       return 0;
+}
+EXPORT_SYMBOL(kstrtoint);
+
+int kstrtou16(const char *s, unsigned int base, u16 *res)
+{
+       unsigned long long tmp;
+       int rv;
+
+       rv = kstrtoull(s, base, &tmp);
+       if (rv < 0)
+               return rv;
+       if (tmp != (unsigned long long)(u16)tmp)
+               return -ERANGE;
+       *res = tmp;
+       return 0;
+}
+EXPORT_SYMBOL(kstrtou16);
+
+int kstrtos16(const char *s, unsigned int base, s16 *res)
+{
+       long long tmp;
+       int rv;
+
+       rv = kstrtoll(s, base, &tmp);
+       if (rv < 0)
+               return rv;
+       if (tmp != (long long)(s16)tmp)
+               return -ERANGE;
+       *res = tmp;
+       return 0;
+}
+EXPORT_SYMBOL(kstrtos16);
+
+int kstrtou8(const char *s, unsigned int base, u8 *res)
+{
+       unsigned long long tmp;
+       int rv;
+
+       rv = kstrtoull(s, base, &tmp);
+       if (rv < 0)
+               return rv;
+       if (tmp != (unsigned long long)(u8)tmp)
+               return -ERANGE;
+       *res = tmp;
+       return 0;
+}
+EXPORT_SYMBOL(kstrtou8);
+
+int kstrtos8(const char *s, unsigned int base, s8 *res)
+{
+       long long tmp;
+       int rv;
+
+       rv = kstrtoll(s, base, &tmp);
+       if (rv < 0)
+               return rv;
+       if (tmp != (long long)(s8)tmp)
+               return -ERANGE;
+       *res = tmp;
+       return 0;
+}
+EXPORT_SYMBOL(kstrtos8);