mirror of
https://github.com/intel/llvm.git
synced 2026-01-13 02:38:07 +08:00
[libc] Templatize strtointeger implementation. (#165884)
* Removes the copy-pasta implementation of wcstointeger, and migrate the wcsto* family of functions to use a template version of strtointeger. * Fixes the out-of-bound read in the original implementation(s) when the entire input string consists of whitespaces (then the sign check can access OOB memory) The code is currently slightly peppered with "if constexpr" statements to distinguish between char and wchar_t. We can probably simplify it in subsequent changes by: * using overrides, so that internal::isalnum() is overriden for both char and wchar_t (since C++ luckily allows us to reuse names). * this wouldn't help for direct comparison with literals - for this as a somewhat ugly workaround like is_char_literal(c, '0', L'0')
This commit is contained in:
@@ -179,19 +179,7 @@ add_header_library(
|
||||
DEPENDS
|
||||
.ctype_utils
|
||||
.str_to_num_result
|
||||
libc.hdr.errno_macros
|
||||
libc.src.__support.CPP.limits
|
||||
libc.src.__support.CPP.type_traits
|
||||
libc.src.__support.common
|
||||
)
|
||||
|
||||
add_header_library(
|
||||
wcs_to_integer
|
||||
HDRS
|
||||
wcs_to_integer.h
|
||||
DEPENDS
|
||||
.wctype_utils
|
||||
.str_to_num_result
|
||||
libc.hdr.errno_macros
|
||||
libc.src.__support.CPP.limits
|
||||
libc.src.__support.CPP.type_traits
|
||||
|
||||
@@ -25,36 +25,63 @@
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/str_to_num_result.h"
|
||||
#include "src/__support/uint128.h"
|
||||
#include "src/__support/wctype_utils.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace internal {
|
||||
|
||||
// Returns the idx to the first character in src that is not a whitespace
|
||||
// character (as determined by isspace())
|
||||
// character (as determined by isspace() / iswspace())
|
||||
template <typename CharType>
|
||||
LIBC_INLINE size_t
|
||||
first_non_whitespace(const char *__restrict src,
|
||||
first_non_whitespace(const CharType *__restrict src,
|
||||
size_t src_len = cpp::numeric_limits<size_t>::max()) {
|
||||
size_t src_cur = 0;
|
||||
while (src_cur < src_len && internal::isspace(src[src_cur])) {
|
||||
while (src_cur < src_len) {
|
||||
if constexpr (cpp::is_same_v<CharType, char>) {
|
||||
if (!internal::isspace(src[src_cur]))
|
||||
break;
|
||||
} else {
|
||||
if (!internal::iswspace(src[src_cur]))
|
||||
break;
|
||||
}
|
||||
++src_cur;
|
||||
}
|
||||
return src_cur;
|
||||
}
|
||||
|
||||
// Returns +1, -1, or 0 if 'src' starts with (respectively)
|
||||
// plus sign, minus sign, or neither.
|
||||
template <typename CharType>
|
||||
LIBC_INLINE static int get_sign(const CharType *__restrict src) {
|
||||
if constexpr (cpp::is_same_v<CharType, char>) {
|
||||
return (src[0] == '+') ? 1 : (src[0] == '-' ? -1 : 0);
|
||||
} else {
|
||||
return (src[0] == L'+') ? 1 : (src[0] == L'-' ? -1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
// checks if the next 3 characters of the string pointer are the start of a
|
||||
// hexadecimal number. Does not advance the string pointer.
|
||||
LIBC_INLINE bool
|
||||
is_hex_start(const char *__restrict src,
|
||||
size_t src_len = cpp::numeric_limits<size_t>::max()) {
|
||||
template <typename CharType>
|
||||
LIBC_INLINE static bool is_hex_start(const CharType *__restrict src,
|
||||
size_t src_len) {
|
||||
if (src_len < 3)
|
||||
return false;
|
||||
return *src == '0' && tolower(*(src + 1)) == 'x' && isalnum(*(src + 2)) &&
|
||||
b36_char_to_int(*(src + 2)) < 16;
|
||||
if constexpr (cpp::is_same_v<CharType, char>) {
|
||||
return src[0] == '0' && tolower(src[1]) == 'x' && isalnum(src[2]) &&
|
||||
b36_char_to_int(src[2]) < 16;
|
||||
} else {
|
||||
return src[0] == L'0' && towlower(src[1]) == L'x' && iswalnum(src[2]) &&
|
||||
b36_wchar_to_int(src[2]) < 16;
|
||||
}
|
||||
}
|
||||
|
||||
// Takes the address of the string pointer and parses the base from the start of
|
||||
// it.
|
||||
LIBC_INLINE int infer_base(const char *__restrict src, size_t src_len) {
|
||||
template <typename CharType>
|
||||
LIBC_INLINE static int infer_base(const CharType *__restrict src,
|
||||
size_t src_len) {
|
||||
// A hexadecimal number is defined as "the prefix 0x or 0X followed by a
|
||||
// sequence of the decimal digits and the letters a (or A) through f (or F)
|
||||
// with values 10 through 15 respectively." (C standard 6.4.4.1)
|
||||
@@ -63,8 +90,15 @@ LIBC_INLINE int infer_base(const char *__restrict src, size_t src_len) {
|
||||
// An octal number is defined as "the prefix 0 optionally followed by a
|
||||
// sequence of the digits 0 through 7 only" (C standard 6.4.4.1) and so any
|
||||
// number that starts with 0, including just 0, is an octal number.
|
||||
if (src_len > 0 && src[0] == '0')
|
||||
return 8;
|
||||
if (src_len > 0) {
|
||||
if constexpr (cpp::is_same_v<CharType, char>) {
|
||||
if (src[0] == '0')
|
||||
return 8;
|
||||
} else {
|
||||
if (src[0] == L'0')
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
// A decimal number is defined as beginning "with a nonzero digit and
|
||||
// consist[ing] of a sequence of decimal digits." (C standard 6.4.4.1)
|
||||
return 10;
|
||||
@@ -77,32 +111,27 @@ LIBC_INLINE int infer_base(const char *__restrict src, size_t src_len) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Takes a pointer to a string and the base to convert to. This function is used
|
||||
// as the backend for all of the string to int functions.
|
||||
template <class T>
|
||||
template <typename T, typename CharType>
|
||||
LIBC_INLINE StrToNumResult<T>
|
||||
strtointeger(const char *__restrict src, int base,
|
||||
strtointeger(const CharType *__restrict src, int base,
|
||||
const size_t src_len = cpp::numeric_limits<size_t>::max()) {
|
||||
using ResultType = make_integral_or_big_int_unsigned_t<T>;
|
||||
|
||||
ResultType result = 0;
|
||||
|
||||
bool is_number = false;
|
||||
size_t src_cur = 0;
|
||||
int error_val = 0;
|
||||
|
||||
if (src_len == 0)
|
||||
return {0, 0, 0};
|
||||
|
||||
if (base < 0 || base == 1 || base > 36)
|
||||
return {0, 0, EINVAL};
|
||||
|
||||
src_cur = first_non_whitespace(src, src_len);
|
||||
|
||||
char result_sign = '+';
|
||||
if (src[src_cur] == '+' || src[src_cur] == '-') {
|
||||
result_sign = src[src_cur];
|
||||
++src_cur;
|
||||
size_t src_cur = first_non_whitespace(src, src_len);
|
||||
if (src_cur == src_len) {
|
||||
return {0, 0, 0};
|
||||
}
|
||||
|
||||
int sign = get_sign(src + src_cur);
|
||||
bool is_positive = (sign >= 0);
|
||||
src_cur += (sign != 0);
|
||||
|
||||
if (base == 0)
|
||||
base = infer_base(src + src_cur, src_len - src_cur);
|
||||
|
||||
@@ -110,8 +139,6 @@ strtointeger(const char *__restrict src, int base,
|
||||
src_cur = src_cur + 2;
|
||||
|
||||
constexpr bool IS_UNSIGNED = cpp::is_unsigned_v<T>;
|
||||
const bool is_positive = (result_sign == '+');
|
||||
|
||||
ResultType constexpr NEGATIVE_MAX =
|
||||
!IS_UNSIGNED ? static_cast<ResultType>(cpp::numeric_limits<T>::max()) + 1
|
||||
: cpp::numeric_limits<T>::max();
|
||||
@@ -120,8 +147,21 @@ strtointeger(const char *__restrict src, int base,
|
||||
ResultType const abs_max_div_by_base =
|
||||
abs_max / static_cast<ResultType>(base);
|
||||
|
||||
while (src_cur < src_len && isalnum(src[src_cur])) {
|
||||
int cur_digit = b36_char_to_int(src[src_cur]);
|
||||
bool is_number = false;
|
||||
int error_val = 0;
|
||||
ResultType result = 0;
|
||||
while (src_cur < src_len) {
|
||||
int cur_digit;
|
||||
if constexpr (cpp::is_same_v<CharType, char>) {
|
||||
if (!isalnum(src[src_cur]))
|
||||
break;
|
||||
cur_digit = b36_char_to_int(src[src_cur]);
|
||||
} else {
|
||||
if (!iswalnum(src[src_cur]))
|
||||
break;
|
||||
cur_digit = b36_wchar_to_int(src[src_cur]);
|
||||
}
|
||||
|
||||
if (cur_digit >= base)
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
//===-- Widechar string to integer conversion utils -------------*- 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___SUPPORT_WCS_TO_INTEGER_H
|
||||
#define LLVM_LIBC_SRC___SUPPORT_WCS_TO_INTEGER_H
|
||||
|
||||
#include "hdr/errno_macros.h" // For ERANGE
|
||||
#include "src/__support/CPP/limits.h"
|
||||
#include "src/__support/CPP/type_traits.h"
|
||||
#include "src/__support/CPP/type_traits/make_unsigned.h"
|
||||
#include "src/__support/big_int.h"
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/str_to_num_result.h"
|
||||
#include "src/__support/uint128.h"
|
||||
#include "src/__support/wctype_utils.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace internal {
|
||||
|
||||
// Returns the idx of the first character in src that is not a whitespace
|
||||
// character (as determined by iswspace())
|
||||
LIBC_INLINE size_t
|
||||
first_non_whitespace(const wchar_t *__restrict src,
|
||||
size_t src_len = cpp::numeric_limits<size_t>::max()) {
|
||||
size_t src_cur = 0;
|
||||
while (src_cur < src_len && internal::iswspace(src[src_cur])) {
|
||||
++src_cur;
|
||||
}
|
||||
return src_cur;
|
||||
}
|
||||
|
||||
// checks if the next 3 characters of the string pointer are the start of a
|
||||
// hexadecimal number. Does not advance the string pointer.
|
||||
LIBC_INLINE bool
|
||||
is_hex_start(const wchar_t *__restrict src,
|
||||
size_t src_len = cpp::numeric_limits<size_t>::max()) {
|
||||
if (src_len < 3)
|
||||
return false;
|
||||
return *src == L'0' && towlower(*(src + 1)) == L'x' && iswalnum(*(src + 2)) &&
|
||||
b36_wchar_to_int(*(src + 2)) < 16;
|
||||
}
|
||||
|
||||
// Takes the address of the string pointer and parses the base from the start of
|
||||
// it.
|
||||
LIBC_INLINE int infer_base(const wchar_t *__restrict src, size_t src_len) {
|
||||
// A hexadecimal number is defined as "the prefix 0x or 0X followed by a
|
||||
// sequence of the decimal digits and the letters a (or A) through f (or F)
|
||||
// with values 10 through 15 respectively." (C standard 6.4.4.1)
|
||||
if (is_hex_start(src, src_len))
|
||||
return 16;
|
||||
// An octal number is defined as "the prefix 0 optionally followed by a
|
||||
// sequence of the digits 0 through 7 only" (C standard 6.4.4.1) and so any
|
||||
// number that starts with 0, including just 0, is an octal number.
|
||||
if (src_len > 0 && src[0] == L'0')
|
||||
return 8;
|
||||
// A decimal number is defined as beginning "with a nonzero digit and
|
||||
// consist[ing] of a sequence of decimal digits." (C standard 6.4.4.1)
|
||||
return 10;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
LIBC_INLINE StrToNumResult<T>
|
||||
wcstointeger(const wchar_t *__restrict src, int base,
|
||||
const size_t src_len = cpp::numeric_limits<size_t>::max()) {
|
||||
using ResultType = make_integral_or_big_int_unsigned_t<T>;
|
||||
|
||||
ResultType result = 0;
|
||||
|
||||
bool is_number = false;
|
||||
size_t src_cur = 0;
|
||||
int error_val = 0;
|
||||
|
||||
if (src_len == 0)
|
||||
return {0, 0, 0};
|
||||
|
||||
if (base < 0 || base == 1 || base > 36)
|
||||
return {0, 0, EINVAL};
|
||||
|
||||
src_cur = first_non_whitespace(src, src_len);
|
||||
|
||||
wchar_t result_sign = L'+';
|
||||
if (src[src_cur] == L'+' || src[src_cur] == L'-') {
|
||||
result_sign = src[src_cur];
|
||||
++src_cur;
|
||||
}
|
||||
|
||||
if (base == 0)
|
||||
base = infer_base(src + src_cur, src_len - src_cur);
|
||||
|
||||
if (base == 16 && is_hex_start(src + src_cur, src_len - src_cur))
|
||||
src_cur = src_cur + 2;
|
||||
|
||||
constexpr bool IS_UNSIGNED = cpp::is_unsigned_v<T>;
|
||||
const bool is_positive = (result_sign == L'+');
|
||||
|
||||
ResultType constexpr NEGATIVE_MAX =
|
||||
!IS_UNSIGNED ? static_cast<ResultType>(cpp::numeric_limits<T>::max()) + 1
|
||||
: cpp::numeric_limits<T>::max();
|
||||
ResultType const abs_max =
|
||||
(is_positive ? cpp::numeric_limits<T>::max() : NEGATIVE_MAX);
|
||||
ResultType const abs_max_div_by_base =
|
||||
abs_max / static_cast<ResultType>(base);
|
||||
|
||||
while (src_cur < src_len && iswalnum(src[src_cur])) {
|
||||
int cur_digit = b36_wchar_to_int(src[src_cur]);
|
||||
if (cur_digit >= base)
|
||||
break;
|
||||
|
||||
is_number = true;
|
||||
++src_cur;
|
||||
|
||||
// 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) {
|
||||
error_val = ERANGE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (result > abs_max_div_by_base) {
|
||||
result = abs_max;
|
||||
error_val = ERANGE;
|
||||
} else {
|
||||
result = result * static_cast<ResultType>(base);
|
||||
}
|
||||
if (result > abs_max - static_cast<ResultType>(cur_digit)) {
|
||||
result = abs_max;
|
||||
error_val = ERANGE;
|
||||
} else {
|
||||
result = result + static_cast<ResultType>(cur_digit);
|
||||
}
|
||||
}
|
||||
|
||||
ptrdiff_t str_len = is_number ? static_cast<ptrdiff_t>(src_cur) : 0;
|
||||
|
||||
if (error_val == ERANGE) {
|
||||
if (is_positive || IS_UNSIGNED)
|
||||
return {cpp::numeric_limits<T>::max(), str_len, error_val};
|
||||
else // T is signed and there is a negative overflow
|
||||
return {cpp::numeric_limits<T>::min(), str_len, error_val};
|
||||
}
|
||||
|
||||
return {static_cast<T>(is_positive ? result : -result), str_len, error_val};
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
#endif // LLVM_LIBC_SRC___SUPPORT_WCS_TO_INTEGER_H
|
||||
@@ -63,7 +63,7 @@ add_entrypoint_object(
|
||||
wcstol.h
|
||||
DEPENDS
|
||||
libc.src.errno.errno
|
||||
libc.src.__support.wcs_to_integer
|
||||
libc.src.__support.str_to_integer
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
@@ -74,7 +74,7 @@ add_entrypoint_object(
|
||||
wcstoll.h
|
||||
DEPENDS
|
||||
libc.src.errno.errno
|
||||
libc.src.__support.wcs_to_integer
|
||||
libc.src.__support.str_to_integer
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
@@ -85,7 +85,7 @@ add_entrypoint_object(
|
||||
wcstoul.h
|
||||
DEPENDS
|
||||
libc.src.errno.errno
|
||||
libc.src.__support.wcs_to_integer
|
||||
libc.src.__support.str_to_integer
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
@@ -96,7 +96,7 @@ add_entrypoint_object(
|
||||
wcstoull.h
|
||||
DEPENDS
|
||||
libc.src.errno.errno
|
||||
libc.src.__support.wcs_to_integer
|
||||
libc.src.__support.str_to_integer
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/libc_errno.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/wcs_to_integer.h"
|
||||
#include "src/__support/str_to_integer.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
LLVM_LIBC_FUNCTION(long, wcstol,
|
||||
(const wchar_t *__restrict str, wchar_t **__restrict str_end,
|
||||
int base)) {
|
||||
auto result = internal::wcstointeger<long>(str, base);
|
||||
auto result = internal::strtointeger<long>(str, base);
|
||||
if (result.has_error())
|
||||
libc_errno = result.error;
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/libc_errno.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/wcs_to_integer.h"
|
||||
#include "src/__support/str_to_integer.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
LLVM_LIBC_FUNCTION(long long, wcstoll,
|
||||
(const wchar_t *__restrict str, wchar_t **__restrict str_end,
|
||||
int base)) {
|
||||
auto result = internal::wcstointeger<long long>(str, base);
|
||||
auto result = internal::strtointeger<long long>(str, base);
|
||||
if (result.has_error())
|
||||
libc_errno = result.error;
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/libc_errno.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/wcs_to_integer.h"
|
||||
#include "src/__support/str_to_integer.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
LLVM_LIBC_FUNCTION(unsigned long, wcstoul,
|
||||
(const wchar_t *__restrict str, wchar_t **__restrict str_end,
|
||||
int base)) {
|
||||
auto result = internal::wcstointeger<unsigned long>(str, base);
|
||||
auto result = internal::strtointeger<unsigned long>(str, base);
|
||||
if (result.has_error())
|
||||
libc_errno = result.error;
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/libc_errno.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
#include "src/__support/wcs_to_integer.h"
|
||||
#include "src/__support/str_to_integer.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
LLVM_LIBC_FUNCTION(unsigned long long, wcstoull,
|
||||
(const wchar_t *__restrict str, wchar_t **__restrict str_end,
|
||||
int base)) {
|
||||
auto result = internal::wcstointeger<unsigned long long>(str, base);
|
||||
auto result = internal::strtointeger<unsigned long long>(str, base);
|
||||
if (result.has_error())
|
||||
libc_errno = result.error;
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ add_libc_test(
|
||||
wcs_to_integer_test.cpp
|
||||
DEPENDS
|
||||
libc.src.__support.integer_literals
|
||||
libc.src.__support.wcs_to_integer
|
||||
libc.src.__support.str_to_integer
|
||||
)
|
||||
|
||||
add_libc_test(
|
||||
|
||||
@@ -49,12 +49,14 @@ TEST(LlvmLibcStrToIntegerTest, LeadingSpaces) {
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
|
||||
ASSERT_EQ(result.value, 12);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(" 12345", 10, 5);
|
||||
// Use a non-null-terminated buffer to test for possible OOB access.
|
||||
char buf[5] = {' ', ' ', ' ', ' ', ' '};
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(buf, 10, 5);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(" 12345", 10, 0);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(buf, 10, 0);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/__support/wcs_to_integer.h"
|
||||
#include "src/__support/str_to_integer.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#include "test/UnitTest/Test.h"
|
||||
@@ -14,224 +14,226 @@
|
||||
// This file is for testing the src_len argument and other internal interface
|
||||
// features. Primary testing is done through the public interface.
|
||||
|
||||
TEST(LlvmLibcStrToIntegerTest, SimpleLength) {
|
||||
auto result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"12345", 10, 10);
|
||||
TEST(LlvmLibcWcsToIntegerTest, SimpleLength) {
|
||||
auto result = LIBC_NAMESPACE::internal::strtointeger<int>(L"12345", 10, 10);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(5));
|
||||
ASSERT_EQ(result.value, 12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"12345", 10, 2);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"12345", 10, 2);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(2));
|
||||
ASSERT_EQ(result.value, 12);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"12345", 10, 0);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"12345", 10, 0);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcStrToIntegerTest, LeadingSpaces) {
|
||||
TEST(LlvmLibcWcsToIntegerTest, LeadingSpaces) {
|
||||
auto result =
|
||||
LIBC_NAMESPACE::internal::wcstointeger<int>(L" 12345", 10, 15);
|
||||
LIBC_NAMESPACE::internal::strtointeger<int>(L" 12345", 10, 15);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(10));
|
||||
ASSERT_EQ(result.value, 12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L" 12345", 10, 10);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L" 12345", 10, 10);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(10));
|
||||
ASSERT_EQ(result.value, 12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L" 12345", 10, 7);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L" 12345", 10, 7);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
|
||||
ASSERT_EQ(result.value, 12);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L" 12345", 10, 5);
|
||||
// Use a non-null-terminated buffer to test for possible OOB access.
|
||||
wchar_t buf[5] = {L' ', L' ', L' ', L' ', L' '};
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(buf, 10, 5);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L" 12345", 10, 0);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(buf, 10, 0);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcStrToIntegerTest, LeadingSign) {
|
||||
auto result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"+12345", 10, 10);
|
||||
TEST(LlvmLibcWcsToIntegerTest, LeadingSign) {
|
||||
auto result = LIBC_NAMESPACE::internal::strtointeger<int>(L"+12345", 10, 10);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
|
||||
ASSERT_EQ(result.value, 12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"-12345", 10, 10);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"-12345", 10, 10);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
|
||||
ASSERT_EQ(result.value, -12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"+12345", 10, 6);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"+12345", 10, 6);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
|
||||
ASSERT_EQ(result.value, 12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"-12345", 10, 6);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"-12345", 10, 6);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
|
||||
ASSERT_EQ(result.value, -12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"+12345", 10, 3);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"+12345", 10, 3);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(3));
|
||||
ASSERT_EQ(result.value, 12);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"-12345", 10, 3);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"-12345", 10, 3);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(3));
|
||||
ASSERT_EQ(result.value, -12);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"+12345", 10, 1);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"+12345", 10, 1);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"-12345", 10, 1);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"-12345", 10, 1);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"+12345", 10, 0);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"+12345", 10, 0);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"-12345", 10, 0);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"-12345", 10, 0);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcStrToIntegerTest, Base16PrefixAutoSelect) {
|
||||
auto result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 0, 10);
|
||||
TEST(LlvmLibcWcsToIntegerTest, Base16PrefixAutoSelect) {
|
||||
auto result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 0, 10);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
|
||||
ASSERT_EQ(result.value, 0x12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 0, 7);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 0, 7);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
|
||||
ASSERT_EQ(result.value, 0x12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 0, 5);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 0, 5);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(5));
|
||||
ASSERT_EQ(result.value, 0x123);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 0, 2);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 0, 2);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(1));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 0, 0);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 0, 0);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcStrToIntegerTest, Base16PrefixManualSelect) {
|
||||
auto result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 16, 10);
|
||||
TEST(LlvmLibcWcsToIntegerTest, Base16PrefixManualSelect) {
|
||||
auto result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 16, 10);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
|
||||
ASSERT_EQ(result.value, 0x12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 16, 7);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 16, 7);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(7));
|
||||
ASSERT_EQ(result.value, 0x12345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 16, 5);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 16, 5);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(5));
|
||||
ASSERT_EQ(result.value, 0x123);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 16, 2);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 16, 2);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(1));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"0x12345", 16, 0);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"0x12345", 16, 0);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcStrToIntegerTest, Base8PrefixAutoSelect) {
|
||||
auto result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 0, 10);
|
||||
TEST(LlvmLibcWcsToIntegerTest, Base8PrefixAutoSelect) {
|
||||
auto result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 0, 10);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
|
||||
ASSERT_EQ(result.value, 012345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 0, 6);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 0, 6);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
|
||||
ASSERT_EQ(result.value, 012345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 0, 4);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 0, 4);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(4));
|
||||
ASSERT_EQ(result.value, 0123);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 0, 1);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 0, 1);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(1));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 0, 0);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 0, 0);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcStrToIntegerTest, Base8PrefixManualSelect) {
|
||||
auto result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 8, 10);
|
||||
TEST(LlvmLibcWcsToIntegerTest, Base8PrefixManualSelect) {
|
||||
auto result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 8, 10);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
|
||||
ASSERT_EQ(result.value, 012345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 8, 6);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 8, 6);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
|
||||
ASSERT_EQ(result.value, 012345);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 8, 4);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 8, 4);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(4));
|
||||
ASSERT_EQ(result.value, 0123);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 8, 1);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 8, 1);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(1));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L"012345", 8, 0);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L"012345", 8, 0);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(0));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
}
|
||||
|
||||
TEST(LlvmLibcStrToIntegerTest, CombinedTests) {
|
||||
TEST(LlvmLibcWcsToIntegerTest, CombinedTests) {
|
||||
auto result =
|
||||
LIBC_NAMESPACE::internal::wcstointeger<int>(L" -0x123", 0, 10);
|
||||
LIBC_NAMESPACE::internal::strtointeger<int>(L" -0x123", 0, 10);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(10));
|
||||
ASSERT_EQ(result.value, -0x123);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L" -0x123", 0, 8);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L" -0x123", 0, 8);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(8));
|
||||
ASSERT_EQ(result.value, -0x1);
|
||||
|
||||
result = LIBC_NAMESPACE::internal::wcstointeger<int>(L" -0x123", 0, 7);
|
||||
result = LIBC_NAMESPACE::internal::strtointeger<int>(L" -0x123", 0, 7);
|
||||
EXPECT_FALSE(result.has_error());
|
||||
EXPECT_EQ(result.parsed_len, ptrdiff_t(6));
|
||||
ASSERT_EQ(result.value, 0);
|
||||
|
||||
@@ -1000,6 +1000,7 @@ libc_support_library(
|
||||
":__support_ctype_utils",
|
||||
":__support_str_to_num_result",
|
||||
":__support_uint128",
|
||||
":__support_wctype_utils",
|
||||
":hdr_errno_macros",
|
||||
],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user