[libc-commits] [libc] d52f0ae - [libc] Add strtol, strtoul, and strtoull

Michael Jones via libc-commits libc-commits at lists.llvm.org
Tue Aug 17 14:05:41 PDT 2021


Author: Michael Jones
Date: 2021-08-17T21:05:37Z
New Revision: d52f0aeca5dbde61aadbcd20e1b8cfaf15a84a65

URL: https://github.com/llvm/llvm-project/commit/d52f0aeca5dbde61aadbcd20e1b8cfaf15a84a65
DIFF: https://github.com/llvm/llvm-project/commit/d52f0aeca5dbde61aadbcd20e1b8cfaf15a84a65.diff

LOG: [libc] Add strtol, strtoul, and strtoull

Updates the internal string conversion function so that it
uses the new Limits.h added in a previous commit for max and min values,
and has a templated type. This makes implementing the other strto*
functions very simple.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D107999

Added: 
    libc/src/stdlib/strtol.cpp
    libc/src/stdlib/strtol.h
    libc/src/stdlib/strtoul.cpp
    libc/src/stdlib/strtoul.h
    libc/src/stdlib/strtoull.cpp
    libc/src/stdlib/strtoull.h
    libc/test/src/stdlib/strtol_test.cpp
    libc/test/src/stdlib/strtoul_test.cpp
    libc/test/src/stdlib/strtoull_test.cpp

Modified: 
    libc/config/linux/x86_64/entrypoints.txt
    libc/spec/spec.td
    libc/spec/stdc.td
    libc/src/__support/CMakeLists.txt
    libc/src/__support/str_conv_utils.h
    libc/src/stdlib/CMakeLists.txt
    libc/src/stdlib/strtoll.cpp
    libc/test/src/stdlib/CMakeLists.txt
    libc/test/src/stdlib/strtoll_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 90346b4b3461..6e163c11c909 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -158,7 +158,10 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.stdlib.abs
     libc.src.stdlib.labs
     libc.src.stdlib.llabs
+    libc.src.stdlib.strtol
     libc.src.stdlib.strtoll
+    libc.src.stdlib.strtoul
+    libc.src.stdlib.strtoull
 
     # signal.h entrypoints
     libc.src.signal.raise

diff  --git a/libc/spec/spec.td b/libc/spec/spec.td
index e49c759c1f79..242303de8e3b 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -40,7 +40,9 @@ def VarArgType : Type {}
 def VoidType : NamedType<"void">;
 def IntType : NamedType<"int">;
 def LongType : NamedType<"long">;
+def UnsignedLongType : NamedType<"unsigned long">;
 def LongLongType : NamedType<"long long">;
+def UnsignedLongLongType : NamedType<"unsigned long long">;
 def FloatType : NamedType<"float">;
 def DoubleType : NamedType<"double">;
 def LongDoubleType : NamedType<"long double">;

diff  --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index eb0ffdfe2e9d..6aa68a3e30c1 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -478,7 +478,10 @@ def StdC : StandardSpec<"stdc"> {
           FunctionSpec<"abs", RetValSpec<IntType>, [ArgSpec<IntType>]>,
           FunctionSpec<"labs", RetValSpec<LongType>, [ArgSpec<LongType>]>,
           FunctionSpec<"llabs", RetValSpec<LongLongType>, [ArgSpec<LongLongType>]>,
+          FunctionSpec<"strtol", RetValSpec<LongType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>, ArgSpec<IntType>]>,
           FunctionSpec<"strtoll", RetValSpec<LongLongType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>, ArgSpec<IntType>]>,
+          FunctionSpec<"strtoul", RetValSpec<UnsignedLongType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>, ArgSpec<IntType>]>,
+          FunctionSpec<"strtoull", RetValSpec<UnsignedLongLongType>, [ArgSpec<ConstCharRestrictedPtr>, ArgSpec<CharRestrictedPtrPtr>, ArgSpec<IntType>]>,
           FunctionSpec<"_Exit", RetValSpec<NoReturn>, [ArgSpec<IntType>]>,
       ]
   >;

diff  --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt
index dce6cb871ec5..a09ca4bcee6f 100644
--- a/libc/src/__support/CMakeLists.txt
+++ b/libc/src/__support/CMakeLists.txt
@@ -20,6 +20,7 @@ add_header_library(
     .ctype_utils
     libc.include.errno
     libc.src.errno.__errno_location
+    libc.utils.CPP.standalone_cpp
 )
 
 add_header_library(

diff  --git a/libc/src/__support/str_conv_utils.h b/libc/src/__support/str_conv_utils.h
index 6a83fa6799a7..adde15823ef6 100644
--- a/libc/src/__support/str_conv_utils.h
+++ b/libc/src/__support/str_conv_utils.h
@@ -10,6 +10,7 @@
 #define LIBC_SRC_STDLIB_STDLIB_UTILS_H
 
 #include "src/__support/ctype_utils.h"
+#include "utils/CPP/Limits.h"
 #include <errno.h>
 #include <limits.h>
 
@@ -50,8 +51,9 @@ static inline int infer_base(const char *__restrict *__restrict src) {
 // Takes a pointer to a string, a pointer to a string pointer, and the base to
 // convert to. This function is used as the backend for all of the string to int
 // functions.
-static inline long long strtoll(const char *__restrict src,
-                                char **__restrict str_end, int base) {
+template <class T>
+static inline T strtointeger(const char *__restrict src,
+                             char **__restrict str_end, int base) {
   unsigned long long result = 0;
 
   if (base < 0 || base == 1 || base > 36) {
@@ -73,36 +75,56 @@ static inline long long strtoll(const char *__restrict src,
     src = src + 2;
   }
 
+  constexpr bool is_unsigned = (__llvm_libc::cpp::NumericLimits<T>::min() == 0);
+  const bool is_positive = (result_sign == '+');
+  unsigned long long constexpr NEGATIVE_MAX =
+      !is_unsigned ? static_cast<unsigned long long>(
+                         __llvm_libc::cpp::NumericLimits<T>::max()) +
+                         1
+                   : __llvm_libc::cpp::NumericLimits<T>::max();
   unsigned long long const ABS_MAX =
-      (result_sign == '+' ? LLONG_MAX
-                          : static_cast<unsigned long long>(LLONG_MAX) + 1);
+      (is_positive ? __llvm_libc::cpp::NumericLimits<T>::max() : NEGATIVE_MAX);
   unsigned long long const ABS_MAX_DIV_BY_BASE = ABS_MAX / base;
   while (isalnum(*src)) {
     int cur_digit = b36_char_to_int(*src);
     if (cur_digit >= base)
       break;
+
+    ++src;
+
+    // If the number has already hit the maximum value for the current type then
+    // the result cannot change, but we still need to advance src to the end of
+    // the number.
+    if (result == ABS_MAX) {
+      errno = ERANGE; // NOLINT
+      continue;
+    }
+
     if (result > ABS_MAX_DIV_BY_BASE) {
       result = ABS_MAX;
       errno = ERANGE; // NOLINT
-      break;
+    } else {
+      result = result * base;
     }
-    result = result * base;
     if (result > ABS_MAX - cur_digit) {
       result = ABS_MAX;
       errno = ERANGE; // NOLINT
-      break;
+    } else {
+      result = result + cur_digit;
     }
-    result = result + cur_digit;
-
-    ++src;
   }
 
   if (str_end != nullptr)
     *str_end = const_cast<char *>(src);
-  if (result_sign == '+')
-    return result;
-  else
-    return -result;
+
+  if (result == ABS_MAX) {
+    if (is_positive || is_unsigned)
+      return __llvm_libc::cpp::NumericLimits<T>::max();
+    else // T is signed and there is a negative overflow
+      return __llvm_libc::cpp::NumericLimits<T>::min();
+  }
+
+  return is_positive ? static_cast<T>(result) : -static_cast<T>(result);
 }
 
 } // namespace internal

diff  --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index 8457310897f5..642202dde1bd 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -51,6 +51,16 @@ add_entrypoint_object(
     libc.src.__support.integer_operations
 )
 
+add_entrypoint_object(
+  strtol
+  SRCS
+    strtol.cpp
+  HDRS
+    strtol.h
+  DEPENDS
+    libc.src.__support.str_conv_utils
+)
+
 add_entrypoint_object(
   strtoll
   SRCS
@@ -60,3 +70,23 @@ add_entrypoint_object(
   DEPENDS
     libc.src.__support.str_conv_utils
 )
+
+add_entrypoint_object(
+  strtoul
+  SRCS
+    strtoul.cpp
+  HDRS
+    strtoul.h
+  DEPENDS
+    libc.src.__support.str_conv_utils
+)
+
+add_entrypoint_object(
+  strtoull
+  SRCS
+    strtoull.cpp
+  HDRS
+    strtoull.h
+  DEPENDS
+    libc.src.__support.str_conv_utils
+)

diff  --git a/libc/src/stdlib/strtol.cpp b/libc/src/stdlib/strtol.cpp
new file mode 100644
index 000000000000..1c744c929e28
--- /dev/null
+++ b/libc/src/stdlib/strtol.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of strtol ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/strtol.h"
+#include "src/__support/common.h"
+#include "src/__support/str_conv_utils.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(long, strtol,
+                   (const char *__restrict str, char **__restrict str_end,
+                    int base)) {
+  return internal::strtointeger<long>(str, str_end, base);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/stdlib/strtol.h b/libc/src/stdlib/strtol.h
new file mode 100644
index 000000000000..741c4e802814
--- /dev/null
+++ b/libc/src/stdlib/strtol.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for strtol ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDLIB_STRTOL_H
+#define LLVM_LIBC_SRC_STDLIB_STRTOL_H
+
+namespace __llvm_libc {
+
+long strtol(const char *__restrict str, char **__restrict str_end, int base);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDLIB_STRTOL_H

diff  --git a/libc/src/stdlib/strtoll.cpp b/libc/src/stdlib/strtoll.cpp
index c99106d7c423..e2fc37f8bf82 100644
--- a/libc/src/stdlib/strtoll.cpp
+++ b/libc/src/stdlib/strtoll.cpp
@@ -15,7 +15,7 @@ namespace __llvm_libc {
 LLVM_LIBC_FUNCTION(long long, strtoll,
                    (const char *__restrict str, char **__restrict str_end,
                     int base)) {
-  return internal::strtoll(str, str_end, base);
+  return internal::strtointeger<long long>(str, str_end, base);
 }
 
 } // namespace __llvm_libc

diff  --git a/libc/src/stdlib/strtoul.cpp b/libc/src/stdlib/strtoul.cpp
new file mode 100644
index 000000000000..eab264f33347
--- /dev/null
+++ b/libc/src/stdlib/strtoul.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of strtoul -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/strtoul.h"
+#include "src/__support/common.h"
+#include "src/__support/str_conv_utils.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(unsigned long, strtoul,
+                   (const char *__restrict str, char **__restrict str_end,
+                    int base)) {
+  return internal::strtointeger<unsigned long>(str, str_end, base);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/stdlib/strtoul.h b/libc/src/stdlib/strtoul.h
new file mode 100644
index 000000000000..6aee32f48f6e
--- /dev/null
+++ b/libc/src/stdlib/strtoul.h
@@ -0,0 +1,19 @@
+//===-- Implementation header for strtoul -----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDLIB_STRTOUL_H
+#define LLVM_LIBC_SRC_STDLIB_STRTOUL_H
+
+namespace __llvm_libc {
+
+unsigned long strtoul(const char *__restrict str, char **__restrict str_end,
+                      int base);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDLIB_STRTOUL_H

diff  --git a/libc/src/stdlib/strtoull.cpp b/libc/src/stdlib/strtoull.cpp
new file mode 100644
index 000000000000..bece2787ba7e
--- /dev/null
+++ b/libc/src/stdlib/strtoull.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of strtoull ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/strtoull.h"
+#include "src/__support/common.h"
+#include "src/__support/str_conv_utils.h"
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(unsigned long long, strtoull,
+                   (const char *__restrict str, char **__restrict str_end,
+                    int base)) {
+  return internal::strtointeger<unsigned long long>(str, str_end, base);
+}
+
+} // namespace __llvm_libc

diff  --git a/libc/src/stdlib/strtoull.h b/libc/src/stdlib/strtoull.h
new file mode 100644
index 000000000000..b64c7be1eb97
--- /dev/null
+++ b/libc/src/stdlib/strtoull.h
@@ -0,0 +1,19 @@
+//===-- Implementation header for strtoull ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDLIB_STRTOULL_H
+#define LLVM_LIBC_SRC_STDLIB_STRTOULL_H
+
+namespace __llvm_libc {
+
+unsigned long long strtoull(const char *__restrict str,
+                            char **__restrict str_end, int base);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDLIB_STRTOULL_H

diff  --git a/libc/test/src/stdlib/CMakeLists.txt b/libc/test/src/stdlib/CMakeLists.txt
index d95f95b8d79e..648d85552bed 100644
--- a/libc/test/src/stdlib/CMakeLists.txt
+++ b/libc/test/src/stdlib/CMakeLists.txt
@@ -55,6 +55,16 @@ add_libc_unittest(
     libc.src.stdlib.llabs
 )
 
+add_libc_unittest(
+  strtol_test
+  SUITE
+    libc_stdlib_unittests
+  SRCS
+    strtol_test.cpp
+  DEPENDS
+    libc.src.stdlib.strtol
+)
+
 add_libc_unittest(
   strtoll_test
   SUITE
@@ -63,6 +73,24 @@ add_libc_unittest(
     strtoll_test.cpp
   DEPENDS
     libc.src.stdlib.strtoll
-    libc.include.errno
-    libc.test.errno_setter_matcher
+)
+
+add_libc_unittest(
+  strtoul_test
+  SUITE
+    libc_stdlib_unittests
+  SRCS
+    strtoul_test.cpp
+  DEPENDS
+    libc.src.stdlib.strtoul
+)
+
+add_libc_unittest(
+  strtoull_test
+  SUITE
+    libc_stdlib_unittests
+  SRCS
+    strtoull_test.cpp
+  DEPENDS
+    libc.src.stdlib.strtoull
 )

diff  --git a/libc/test/src/stdlib/strtol_test.cpp b/libc/test/src/stdlib/strtol_test.cpp
new file mode 100644
index 000000000000..14a473537b39
--- /dev/null
+++ b/libc/test/src/stdlib/strtol_test.cpp
@@ -0,0 +1,289 @@
+//===-- Unittests for strtol ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/strtol.h"
+
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+#include <limits.h>
+
+TEST(LlvmLibcStrToLTest, InvalidBase) {
+  const char *ten = "10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(ten, nullptr, -1), 0l);
+  ASSERT_EQ(errno, EINVAL);
+}
+
+TEST(LlvmLibcStrToLTest, CleanBaseTenDecode) {
+  char *str_end = nullptr;
+
+  const char *ten = "10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(ten, &str_end, 10), 10l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - ten, 2l);
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(ten, nullptr, 10), 10l);
+  ASSERT_EQ(errno, 0);
+
+  const char *hundred = "100";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(hundred, &str_end, 10), 100l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - hundred, 3l);
+
+  const char *negative = "-100";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(negative, &str_end, 10), -100l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - negative, 4l);
+
+  const char *big_number = "123456789012345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(big_number, &str_end, 10), 123456789012345l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - big_number, 15l);
+
+  const char *big_negative_number = "-123456789012345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(big_negative_number, &str_end, 10),
+            -123456789012345l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - big_negative_number, 16l);
+
+  const char *too_big_number = "123456789012345678901";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(too_big_number, &str_end, 10), LONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - too_big_number, 21l);
+
+  const char *too_big_negative_number = "-123456789012345678901";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(too_big_negative_number, &str_end, 10),
+            LONG_MIN);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - too_big_negative_number, 22l);
+
+  const char *long_number_range_test =
+      "10000000000000000000000000000000000000000000000000";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(long_number_range_test, &str_end, 10),
+            LONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - long_number_range_test, 50l);
+}
+
+TEST(LlvmLibcStrToLTest, MessyBaseTenDecode) {
+  char *str_end = nullptr;
+
+  const char *spaces_before = "     10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(spaces_before, &str_end, 10), 10l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - spaces_before, 7l);
+
+  const char *spaces_after = "10      ";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(spaces_after, &str_end, 10), 10l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - spaces_after, 2l);
+
+  const char *word_before = "word10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(word_before, &str_end, 10), 0l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - word_before, 0l);
+
+  const char *word_after = "10word";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(word_after, &str_end, 10), 10l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - word_after, 2l);
+
+  const char *two_numbers = "10 999";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(two_numbers, &str_end, 10), 10l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - two_numbers, 2l);
+
+  const char *two_signs = "--10 999";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(two_signs, &str_end, 10), 0l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - two_signs, 1l);
+
+  const char *sign_before = "+2=4";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(sign_before, &str_end, 10), 2l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - sign_before, 2l);
+
+  const char *sign_after = "2+2=4";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(sign_after, &str_end, 10), 2l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - sign_after, 1l);
+
+  const char *tab_before = "\t10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(tab_before, &str_end, 10), 10l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - tab_before, 3l);
+
+  const char *all_together = "\t  -12345and+67890";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(all_together, &str_end, 10), -12345l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - all_together, 9l);
+}
+
+static char int_to_b36_char(int input) {
+  if (input < 0 || input > 36)
+    return '0';
+  if (input < 10)
+    return '0' + input;
+  return 'A' + input - 10;
+}
+
+TEST(LlvmLibcStrToLTest, DecodeInOtherBases) {
+  char small_string[4] = {'\0', '\0', '\0', '\0'};
+  for (unsigned int base = 2; base <= 36; ++base) {
+    for (long first_digit = 0; first_digit <= 36; ++first_digit) {
+      small_string[0] = int_to_b36_char(first_digit);
+      if (first_digit < base) {
+        errno = 0;
+        ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base),
+                  first_digit);
+        ASSERT_EQ(errno, 0);
+      } else {
+        errno = 0;
+        ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base), 0l);
+        ASSERT_EQ(errno, 0);
+      }
+    }
+  }
+
+  for (unsigned int base = 2; base <= 36; ++base) {
+    for (long first_digit = 0; first_digit <= 36; ++first_digit) {
+      small_string[0] = int_to_b36_char(first_digit);
+      for (long second_digit = 0; second_digit <= 36; ++second_digit) {
+        small_string[1] = int_to_b36_char(second_digit);
+        if (first_digit < base && second_digit < base) {
+          errno = 0;
+          ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base),
+                    second_digit + (first_digit * base));
+          ASSERT_EQ(errno, 0);
+        } else if (first_digit < base) {
+          errno = 0;
+          ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base),
+                    first_digit);
+          ASSERT_EQ(errno, 0);
+        } else {
+          errno = 0;
+          ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base), 0l);
+          ASSERT_EQ(errno, 0);
+        }
+      }
+    }
+  }
+
+  for (unsigned int base = 2; base <= 36; ++base) {
+    for (long first_digit = 0; first_digit <= 36; ++first_digit) {
+      small_string[0] = int_to_b36_char(first_digit);
+      for (long second_digit = 0; second_digit <= 36; ++second_digit) {
+        small_string[1] = int_to_b36_char(second_digit);
+        for (long third_digit = 0; third_digit <= 36; ++third_digit) {
+          small_string[2] = int_to_b36_char(third_digit);
+
+          if (first_digit < base && second_digit < base && third_digit < base) {
+            errno = 0;
+            ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base),
+                      third_digit + (second_digit * base) +
+                          (first_digit * base * base));
+            ASSERT_EQ(errno, 0);
+          } else if (first_digit < base && second_digit < base) {
+            errno = 0;
+            ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base),
+                      second_digit + (first_digit * base));
+            ASSERT_EQ(errno, 0);
+          } else if (first_digit < base) {
+            // if the base is 16 there is a special case for the prefix 0X.
+            // The number is treated as a one digit hexadecimal.
+            if (base == 16 && first_digit == 0 && second_digit == 33) {
+              if (third_digit < base) {
+                errno = 0;
+                ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base),
+                          third_digit);
+                ASSERT_EQ(errno, 0);
+              } else {
+                errno = 0;
+                ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base), 0l);
+                ASSERT_EQ(errno, 0);
+              }
+            } else {
+              errno = 0;
+              ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base),
+                        first_digit);
+              ASSERT_EQ(errno, 0);
+            }
+          } else {
+            errno = 0;
+            ASSERT_EQ(__llvm_libc::strtol(small_string, nullptr, base), 0l);
+            ASSERT_EQ(errno, 0);
+          }
+        }
+      }
+    }
+  }
+}
+
+TEST(LlvmLibcStrToLTest, CleanBaseSixteenDecode) {
+  char *str_end = nullptr;
+
+  const char *no_prefix = "123abc";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(no_prefix, &str_end, 16), 0x123abcl);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - no_prefix, 6l);
+
+  const char *yes_prefix = "0x456def";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(yes_prefix, &str_end, 16), 0x456defl);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - yes_prefix, 8l);
+}
+
+TEST(LlvmLibcStrToLTest, AutomaticBaseSelection) {
+  char *str_end = nullptr;
+
+  const char *base_ten = "12345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(base_ten, &str_end, 0), 12345l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_ten, 5l);
+
+  const char *base_sixteen_no_prefix = "123abc";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(base_sixteen_no_prefix, &str_end, 0), 123l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_sixteen_no_prefix, 3l);
+
+  const char *base_sixteen_with_prefix = "0x456def";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(base_sixteen_with_prefix, &str_end, 0),
+            0x456defl);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_sixteen_with_prefix, 8l);
+
+  const char *base_eight_with_prefix = "012345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtol(base_eight_with_prefix, &str_end, 0), 012345l);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_eight_with_prefix, 6l);
+}

diff  --git a/libc/test/src/stdlib/strtoll_test.cpp b/libc/test/src/stdlib/strtoll_test.cpp
index 437d42f5bab8..ca5467182b7d 100644
--- a/libc/test/src/stdlib/strtoll_test.cpp
+++ b/libc/test/src/stdlib/strtoll_test.cpp
@@ -57,18 +57,49 @@ TEST(LlvmLibcStrToLLTest, CleanBaseTenDecode) {
   ASSERT_EQ(errno, 0);
   EXPECT_EQ(str_end - big_negative_number, 16l);
 
+  const char *long_long_max_number = "9223372036854775807";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoll(long_long_max_number, &str_end, 10),
+            9223372036854775807ll);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - long_long_max_number, 19l);
+
+  const char *long_long_min_number = "-9223372036854775808";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoll(long_long_min_number, &str_end, 10),
+            -9223372036854775807ll - 1ll);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - long_long_min_number, 20l);
+
   const char *too_big_number = "123456789012345678901";
   errno = 0;
   ASSERT_EQ(__llvm_libc::strtoll(too_big_number, &str_end, 10), LLONG_MAX);
   ASSERT_EQ(errno, ERANGE);
-  EXPECT_EQ(str_end - too_big_number, 19l);
+  EXPECT_EQ(str_end - too_big_number, 21l);
 
   const char *too_big_negative_number = "-123456789012345678901";
   errno = 0;
   ASSERT_EQ(__llvm_libc::strtoll(too_big_negative_number, &str_end, 10),
             LLONG_MIN);
   ASSERT_EQ(errno, ERANGE);
-  EXPECT_EQ(str_end - too_big_negative_number, 20l);
+  EXPECT_EQ(str_end - too_big_negative_number, 22l);
+
+  const char *long_number_range_test =
+      "10000000000000000000000000000000000000000000000000";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoll(long_number_range_test, &str_end, 10),
+            LLONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - long_number_range_test, 50l);
+
+  const char *long_long_max_number_with_numbers_after =
+      "9223372036854775807123";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoll(long_long_max_number_with_numbers_after,
+                                 &str_end, 10),
+            LLONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - long_long_max_number_with_numbers_after, 22l);
 }
 
 TEST(LlvmLibcStrToLLTest, MessyBaseTenDecode) {
@@ -145,7 +176,7 @@ static char int_to_b36_char(int input) {
 
 TEST(LlvmLibcStrToLLTest, DecodeInOtherBases) {
   char small_string[4] = {'\0', '\0', '\0', '\0'};
-  for (int base = 2; base <= 36; ++base) {
+  for (unsigned int base = 2; base <= 36; ++base) {
     for (long long first_digit = 0; first_digit <= 36; ++first_digit) {
       small_string[0] = int_to_b36_char(first_digit);
       if (first_digit < base) {
@@ -161,7 +192,7 @@ TEST(LlvmLibcStrToLLTest, DecodeInOtherBases) {
     }
   }
 
-  for (int base = 2; base <= 36; ++base) {
+  for (unsigned int base = 2; base <= 36; ++base) {
     for (long long first_digit = 0; first_digit <= 36; ++first_digit) {
       small_string[0] = int_to_b36_char(first_digit);
       for (long long second_digit = 0; second_digit <= 36; ++second_digit) {
@@ -185,7 +216,7 @@ TEST(LlvmLibcStrToLLTest, DecodeInOtherBases) {
     }
   }
 
-  for (int base = 2; base <= 36; ++base) {
+  for (unsigned int base = 2; base <= 36; ++base) {
     for (long long first_digit = 0; first_digit <= 36; ++first_digit) {
       small_string[0] = int_to_b36_char(first_digit);
       for (long long second_digit = 0; second_digit <= 36; ++second_digit) {

diff  --git a/libc/test/src/stdlib/strtoul_test.cpp b/libc/test/src/stdlib/strtoul_test.cpp
new file mode 100644
index 000000000000..cff208618f50
--- /dev/null
+++ b/libc/test/src/stdlib/strtoul_test.cpp
@@ -0,0 +1,284 @@
+//===-- Unittests for strtoul ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/strtoul.h"
+
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+#include <limits.h>
+
+TEST(LlvmLibcStrToULTest, InvalidBase) {
+  const char *ten = "10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(ten, nullptr, -1), 0ul);
+  ASSERT_EQ(errno, EINVAL);
+}
+
+TEST(LlvmLibcStrToULTest, CleanBaseTenDecode) {
+  char *str_end = nullptr;
+
+  const char *ten = "10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(ten, &str_end, 10), 10ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - ten, 2l);
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(ten, nullptr, 10), 10ul);
+  ASSERT_EQ(errno, 0);
+
+  const char *hundred = "100";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(hundred, &str_end, 10), 100ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - hundred, 3l);
+
+  const char *negative = "-100";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(negative, &str_end, 10), -(100ul));
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - negative, 4l);
+
+  const char *big_number = "123456789012345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(big_number, &str_end, 10), 123456789012345ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - big_number, 15l);
+
+  const char *too_big_number = "123456789012345678901";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(too_big_number, &str_end, 10), ULONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - too_big_number, 21l);
+
+  const char *too_big_negative_number = "-123456789012345678901";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(too_big_negative_number, &str_end, 10),
+            ULONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - too_big_negative_number, 22l);
+
+  const char *long_number_range_test =
+      "10000000000000000000000000000000000000000000000000";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(long_number_range_test, &str_end, 10),
+            ULONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - long_number_range_test, 50l);
+}
+
+TEST(LlvmLibcStrToULTest, MessyBaseTenDecode) {
+  char *str_end = nullptr;
+
+  const char *spaces_before = "     10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(spaces_before, &str_end, 10), 10ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - spaces_before, 7l);
+
+  const char *spaces_after = "10      ";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(spaces_after, &str_end, 10), 10ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - spaces_after, 2l);
+
+  const char *word_before = "word10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(word_before, &str_end, 10), 0ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - word_before, 0l);
+
+  const char *word_after = "10word";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(word_after, &str_end, 10), 10ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - word_after, 2l);
+
+  const char *two_numbers = "10 999";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(two_numbers, &str_end, 10), 10ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - two_numbers, 2l);
+
+  const char *two_signs = "--10 999";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(two_signs, &str_end, 10), 0ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - two_signs, 1l);
+
+  const char *sign_before = "+2=4";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(sign_before, &str_end, 10), 2ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - sign_before, 2l);
+
+  const char *sign_after = "2+2=4";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(sign_after, &str_end, 10), 2ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - sign_after, 1l);
+
+  const char *tab_before = "\t10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(tab_before, &str_end, 10), 10ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - tab_before, 3l);
+
+  const char *all_together = "\t  -12345and+67890";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(all_together, &str_end, 10), -(12345ul));
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - all_together, 9l);
+}
+
+static char int_to_b36_char(int input) {
+  if (input < 0 || input > 36)
+    return '0';
+  if (input < 10)
+    return '0' + input;
+  return 'A' + input - 10;
+}
+
+TEST(LlvmLibcStrToULTest, DecodeInOtherBases) {
+  char small_string[4] = {'\0', '\0', '\0', '\0'};
+  for (unsigned int base = 2; base <= 36; ++base) {
+    for (unsigned long first_digit = 0; first_digit <= 36; ++first_digit) {
+      small_string[0] = int_to_b36_char(first_digit);
+      if (first_digit < base) {
+        errno = 0;
+        ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base),
+                  first_digit);
+        ASSERT_EQ(errno, 0);
+      } else {
+        errno = 0;
+        ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base), 0ul);
+        ASSERT_EQ(errno, 0);
+      }
+    }
+  }
+
+  for (unsigned int base = 2; base <= 36; ++base) {
+    for (unsigned long first_digit = 0; first_digit <= 36; ++first_digit) {
+      small_string[0] = int_to_b36_char(first_digit);
+      for (unsigned long second_digit = 0; second_digit <= 36; ++second_digit) {
+        small_string[1] = int_to_b36_char(second_digit);
+        if (first_digit < base && second_digit < base) {
+          errno = 0;
+          ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base),
+                    second_digit + (first_digit * base));
+          ASSERT_EQ(errno, 0);
+        } else if (first_digit < base) {
+          errno = 0;
+          ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base),
+                    first_digit);
+          ASSERT_EQ(errno, 0);
+        } else {
+          errno = 0;
+          ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base), 0ul);
+          ASSERT_EQ(errno, 0);
+        }
+      }
+    }
+  }
+
+  for (unsigned int base = 2; base <= 36; ++base) {
+    for (unsigned long first_digit = 0; first_digit <= 36; ++first_digit) {
+      small_string[0] = int_to_b36_char(first_digit);
+      for (unsigned long second_digit = 0; second_digit <= 36; ++second_digit) {
+        small_string[1] = int_to_b36_char(second_digit);
+        for (unsigned long third_digit = 0; third_digit <= 36; ++third_digit) {
+          small_string[2] = int_to_b36_char(third_digit);
+
+          if (first_digit < base && second_digit < base && third_digit < base) {
+            errno = 0;
+            ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base),
+                      third_digit + (second_digit * base) +
+                          (first_digit * base * base));
+            ASSERT_EQ(errno, 0);
+          } else if (first_digit < base && second_digit < base) {
+            errno = 0;
+            ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base),
+                      second_digit + (first_digit * base));
+            ASSERT_EQ(errno, 0);
+          } else if (first_digit < base) {
+            // if the base is 16 there is a special case for the prefix 0X.
+            // The number is treated as a one digit hexadecimal.
+            if (base == 16 && first_digit == 0 && second_digit == 33) {
+              if (third_digit < base) {
+                errno = 0;
+                ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base),
+                          third_digit);
+                ASSERT_EQ(errno, 0);
+              } else {
+                errno = 0;
+                ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base),
+                          0ul);
+                ASSERT_EQ(errno, 0);
+              }
+            } else {
+              errno = 0;
+              ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base),
+                        first_digit);
+              ASSERT_EQ(errno, 0);
+            }
+          } else {
+            errno = 0;
+            ASSERT_EQ(__llvm_libc::strtoul(small_string, nullptr, base), 0ul);
+            ASSERT_EQ(errno, 0);
+          }
+        }
+      }
+    }
+  }
+}
+
+TEST(LlvmLibcStrToULTest, CleanBaseSixteenDecode) {
+  char *str_end = nullptr;
+
+  const char *no_prefix = "123abc";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(no_prefix, &str_end, 16), 0x123abcul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - no_prefix, 6l);
+
+  const char *yes_prefix = "0x456def";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(yes_prefix, &str_end, 16), 0x456deful);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - yes_prefix, 8l);
+}
+
+TEST(LlvmLibcStrToULTest, AutomaticBaseSelection) {
+  char *str_end = nullptr;
+
+  const char *base_ten = "12345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(base_ten, &str_end, 0), 12345ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_ten, 5l);
+
+  const char *base_sixteen_no_prefix = "123abc";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(base_sixteen_no_prefix, &str_end, 0), 123ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_sixteen_no_prefix, 3l);
+
+  const char *base_sixteen_with_prefix = "0x456def";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(base_sixteen_with_prefix, &str_end, 0),
+            0x456deful);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_sixteen_with_prefix, 8l);
+
+  const char *base_eight_with_prefix = "012345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoul(base_eight_with_prefix, &str_end, 0),
+            012345ul);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_eight_with_prefix, 6l);
+}

diff  --git a/libc/test/src/stdlib/strtoull_test.cpp b/libc/test/src/stdlib/strtoull_test.cpp
new file mode 100644
index 000000000000..e6b52c92432a
--- /dev/null
+++ b/libc/test/src/stdlib/strtoull_test.cpp
@@ -0,0 +1,295 @@
+//===-- Unittests for strtoull --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/stdlib/strtoull.h"
+
+#include "utils/UnitTest/Test.h"
+
+#include <errno.h>
+#include <limits.h>
+
+TEST(LlvmLibcStrToULLTest, InvalidBase) {
+  const char *ten = "10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(ten, nullptr, -1), 0ull);
+  ASSERT_EQ(errno, EINVAL);
+}
+
+TEST(LlvmLibcStrToULLTest, CleanBaseTenDecode) {
+  char *str_end = nullptr;
+
+  const char *ten = "10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(ten, &str_end, 10), 10ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - ten, 2l);
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(ten, nullptr, 10), 10ull);
+  ASSERT_EQ(errno, 0);
+
+  const char *hundred = "100";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(hundred, &str_end, 10), 100ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - hundred, 3l);
+
+  const char *negative = "-100";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(negative, &str_end, 10), -(100ull));
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - negative, 4l);
+
+  const char *big_number = "123456789012345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(big_number, &str_end, 10),
+            123456789012345ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - big_number, 15l);
+
+  const char *unsigned_long_long_max_number = "18446744073709551615";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(unsigned_long_long_max_number, &str_end, 10),
+            18446744073709551615ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - unsigned_long_long_max_number, 20l);
+
+  const char *too_big_number = "123456789012345678901";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(too_big_number, &str_end, 10), ULLONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - too_big_number, 21l);
+
+  const char *too_big_negative_number = "-123456789012345678901";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(too_big_negative_number, &str_end, 10),
+            ULLONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - too_big_negative_number, 22l);
+
+  const char *long_number_range_test =
+      "10000000000000000000000000000000000000000000000000";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(long_number_range_test, &str_end, 10),
+            ULLONG_MAX);
+  ASSERT_EQ(errno, ERANGE);
+  EXPECT_EQ(str_end - long_number_range_test, 50l);
+}
+
+TEST(LlvmLibcStrToULLTest, MessyBaseTenDecode) {
+  char *str_end = nullptr;
+
+  const char *spaces_before = "     10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(spaces_before, &str_end, 10), 10ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - spaces_before, 7l);
+
+  const char *spaces_after = "10      ";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(spaces_after, &str_end, 10), 10ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - spaces_after, 2l);
+
+  const char *word_before = "word10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(word_before, &str_end, 10), 0ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - word_before, 0l);
+
+  const char *word_after = "10word";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(word_after, &str_end, 10), 10ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - word_after, 2l);
+
+  const char *two_numbers = "10 999";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(two_numbers, &str_end, 10), 10ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - two_numbers, 2l);
+
+  const char *two_signs = "--10 999";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(two_signs, &str_end, 10), 0ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - two_signs, 1l);
+
+  const char *sign_before = "+2=4";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(sign_before, &str_end, 10), 2ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - sign_before, 2l);
+
+  const char *sign_after = "2+2=4";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(sign_after, &str_end, 10), 2ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - sign_after, 1l);
+
+  const char *tab_before = "\t10";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(tab_before, &str_end, 10), 10ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - tab_before, 3l);
+
+  const char *all_together = "\t  -12345and+67890";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(all_together, &str_end, 10), -(12345ull));
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - all_together, 9l);
+}
+
+static char int_to_b36_char(int input) {
+  if (input < 0 || input > 36)
+    return '0';
+  if (input < 10)
+    return '0' + input;
+  return 'A' + input - 10;
+}
+
+TEST(LlvmLibcStrToULLTest, DecodeInOtherBases) {
+  char small_string[4] = {'\0', '\0', '\0', '\0'};
+  for (unsigned int base = 2; base <= 36; ++base) {
+    for (unsigned long long first_digit = 0; first_digit <= 36; ++first_digit) {
+      small_string[0] = int_to_b36_char(first_digit);
+      if (first_digit < base) {
+        errno = 0;
+        ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base),
+                  first_digit);
+        ASSERT_EQ(errno, 0);
+      } else {
+        errno = 0;
+        ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base), 0ull);
+        ASSERT_EQ(errno, 0);
+      }
+    }
+  }
+
+  for (unsigned int base = 2; base <= 36; ++base) {
+    for (unsigned long long first_digit = 0; first_digit <= 36; ++first_digit) {
+      small_string[0] = int_to_b36_char(first_digit);
+      for (unsigned long long second_digit = 0; second_digit <= 36;
+           ++second_digit) {
+        small_string[1] = int_to_b36_char(second_digit);
+        if (first_digit < base && second_digit < base) {
+          errno = 0;
+          ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base),
+                    second_digit + (first_digit * base));
+          ASSERT_EQ(errno, 0);
+        } else if (first_digit < base) {
+          errno = 0;
+          ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base),
+                    first_digit);
+          ASSERT_EQ(errno, 0);
+        } else {
+          errno = 0;
+          ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base), 0ull);
+          ASSERT_EQ(errno, 0);
+        }
+      }
+    }
+  }
+
+  for (unsigned int base = 2; base <= 36; ++base) {
+    for (unsigned long long first_digit = 0; first_digit <= 36; ++first_digit) {
+      small_string[0] = int_to_b36_char(first_digit);
+      for (unsigned long long second_digit = 0; second_digit <= 36;
+           ++second_digit) {
+        small_string[1] = int_to_b36_char(second_digit);
+        for (unsigned long long third_digit = 0; third_digit <= 36;
+             ++third_digit) {
+          small_string[2] = int_to_b36_char(third_digit);
+
+          if (first_digit < base && second_digit < base && third_digit < base) {
+            errno = 0;
+            ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base),
+                      third_digit + (second_digit * base) +
+                          (first_digit * base * base));
+            ASSERT_EQ(errno, 0);
+          } else if (first_digit < base && second_digit < base) {
+            errno = 0;
+            ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base),
+                      second_digit + (first_digit * base));
+            ASSERT_EQ(errno, 0);
+          } else if (first_digit < base) {
+            // if the base is 16 there is a special case for the prefix 0X.
+            // The number is treated as a one digit hexadecimal.
+            if (base == 16 && first_digit == 0 && second_digit == 33) {
+              if (third_digit < base) {
+                errno = 0;
+                ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base),
+                          third_digit);
+                ASSERT_EQ(errno, 0);
+              } else {
+                errno = 0;
+                ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base),
+                          0ull);
+                ASSERT_EQ(errno, 0);
+              }
+            } else {
+              errno = 0;
+              ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base),
+                        first_digit);
+              ASSERT_EQ(errno, 0);
+            }
+          } else {
+            errno = 0;
+            ASSERT_EQ(__llvm_libc::strtoull(small_string, nullptr, base), 0ull);
+            ASSERT_EQ(errno, 0);
+          }
+        }
+      }
+    }
+  }
+}
+
+TEST(LlvmLibcStrToULLTest, CleanBaseSixteenDecode) {
+  char *str_end = nullptr;
+
+  const char *no_prefix = "123abc";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(no_prefix, &str_end, 16), 0x123abcull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - no_prefix, 6l);
+
+  const char *yes_prefix = "0x456def";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(yes_prefix, &str_end, 16), 0x456defull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - yes_prefix, 8l);
+}
+
+TEST(LlvmLibcStrToULLTest, AutomaticBaseSelection) {
+  char *str_end = nullptr;
+
+  const char *base_ten = "12345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(base_ten, &str_end, 0), 12345ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_ten, 5l);
+
+  const char *base_sixteen_no_prefix = "123abc";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(base_sixteen_no_prefix, &str_end, 0), 123ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_sixteen_no_prefix, 3l);
+
+  const char *base_sixteen_with_prefix = "0x456def";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(base_sixteen_with_prefix, &str_end, 0),
+            0x456defull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_sixteen_with_prefix, 8l);
+
+  const char *base_eight_with_prefix = "012345";
+  errno = 0;
+  ASSERT_EQ(__llvm_libc::strtoull(base_eight_with_prefix, &str_end, 0),
+            012345ull);
+  ASSERT_EQ(errno, 0);
+  EXPECT_EQ(str_end - base_eight_with_prefix, 6l);
+}


        


More information about the libc-commits mailing list