21 changes: 8 additions & 13 deletions libc/src/stdio/printf_core/int_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "src/__support/CPP/span.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/integer_to_string.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/converter_utils.h"
Expand All @@ -23,11 +24,6 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {

// These functions only work on characters that are already known to be in the
// alphabet. Their behavior is undefined otherwise.
LIBC_INLINE constexpr char to_lower(char a) { return a | 32; }
LIBC_INLINE constexpr bool is_lower(char a) { return (a & 32) > 0; }

namespace details {

using HexFmt = IntegerToString<uintmax_t, radix::Hex>;
Expand All @@ -49,14 +45,14 @@ LIBC_INLINE constexpr size_t num_buf_size() {

LIBC_INLINE cpp::optional<cpp::string_view>
num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {
if (to_lower(conv_name) == 'x') {
if (is_lower(conv_name))
if (internal::tolower(conv_name) == 'x') {
if (internal::islower(conv_name))
return HexFmt::format_to(bufref, num);
else
return HexFmtUppercase::format_to(bufref, num);
} else if (conv_name == 'o') {
return OctFmt::format_to(bufref, num);
} else if (to_lower(conv_name) == 'b') {
} else if (internal::tolower(conv_name) == 'b') {
return BinFmt::format_to(bufref, num);
} else {
return DecFmt::format_to(bufref, num);
Expand All @@ -72,7 +68,6 @@ LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) {
uintmax_t num = static_cast<uintmax_t>(to_conv.conv_val_raw);
bool is_negative = false;
FormatFlags flags = to_conv.flags;
const char a = is_lower(to_conv.conv_name) ? 'a' : 'A';

// If the conversion is signed, then handle negative values.
if (to_conv.conv_name == 'd' || to_conv.conv_name == 'i') {
Expand Down Expand Up @@ -116,16 +111,16 @@ LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) {
// conversions. Since hexadecimal is unsigned these will never conflict.
size_t prefix_len;
char prefix[2];
if ((to_lower(to_conv.conv_name) == 'x') &&
if ((internal::tolower(to_conv.conv_name) == 'x') &&
((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) {
prefix_len = 2;
prefix[0] = '0';
prefix[1] = a + ('x' - 'a');
} else if ((to_lower(to_conv.conv_name) == 'b') &&
prefix[1] = internal::islower(to_conv.conv_name) ? 'x' : 'X';
} else if ((internal::tolower(to_conv.conv_name) == 'b') &&
((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) {
prefix_len = 2;
prefix[0] = '0';
prefix[1] = a + ('b' - 'a');
prefix[1] = internal::islower(to_conv.conv_name) ? 'b' : 'B';
} else {
prefix_len = (sign_char == 0 ? 0 : 1);
prefix[0] = sign_char;
Expand Down
10 changes: 0 additions & 10 deletions libc/src/stdio/scanf_core/converter_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,6 @@
namespace LIBC_NAMESPACE_DECL {
namespace scanf_core {

LIBC_INLINE constexpr char to_lower(char a) { return a | 32; }

LIBC_INLINE constexpr int b36_char_to_int(char input) {
if (internal::isdigit(input))
return input - '0';
if (internal::isalpha(input))
return to_lower(input) + 10 - 'a';
return 0;
}

LIBC_INLINE void write_int_with_length(uintmax_t output_val,
const FormatSection &to_conv) {
if ((to_conv.flags & NO_WRITE) != 0) {
Expand Down
18 changes: 10 additions & 8 deletions libc/src/stdio/scanf_core/float_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@ int convert_float(Reader *reader, const FormatSection &to_conv) {

// Handle inf

if (to_lower(cur_char) == inf_string[0]) {
if (internal::tolower(cur_char) == inf_string[0]) {
size_t inf_index = 0;

for (; inf_index < sizeof(inf_string) && out_str.length() < max_width &&
to_lower(cur_char) == inf_string[inf_index];
for (;
inf_index < (sizeof(inf_string) - 1) && out_str.length() < max_width &&
internal::tolower(cur_char) == inf_string[inf_index];
++inf_index) {
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
Expand All @@ -78,11 +79,12 @@ int convert_float(Reader *reader, const FormatSection &to_conv) {
static const char nan_string[] = "nan";

// Handle nan
if (to_lower(cur_char) == nan_string[0]) {
if (internal::tolower(cur_char) == nan_string[0]) {
size_t nan_index = 0;

for (; nan_index < sizeof(nan_string) && out_str.length() < max_width &&
to_lower(cur_char) == nan_string[nan_index];
for (;
nan_index < (sizeof(nan_string) - 1) && out_str.length() < max_width &&
internal::tolower(cur_char) == nan_string[nan_index];
++nan_index) {
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
Expand Down Expand Up @@ -117,7 +119,7 @@ int convert_float(Reader *reader, const FormatSection &to_conv) {
}

// If that next character is an 'x' then this is a hexadecimal number.
if (to_lower(cur_char) == 'x') {
if (internal::tolower(cur_char) == 'x') {
base = 16;

if (!out_str.append(cur_char)) {
Expand Down Expand Up @@ -163,7 +165,7 @@ int convert_float(Reader *reader, const FormatSection &to_conv) {

// Handle the exponent, which has an exponent mark, an optional sign, and
// decimal digits.
if (to_lower(cur_char) == exponent_mark) {
if (internal::tolower(cur_char) == exponent_mark) {
if (!out_str.append(cur_char)) {
return ALLOCATION_FAILURE;
}
Expand Down
12 changes: 7 additions & 5 deletions libc/src/stdio/scanf_core/int_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ int convert_int(Reader *reader, const FormatSection &to_conv) {
is_signed = true;
} else if (to_conv.conv_name == 'o') {
base = 8;
} else if (to_lower(to_conv.conv_name) == 'x' || to_conv.conv_name == 'p') {
} else if (internal::tolower(to_conv.conv_name) == 'x' ||
to_conv.conv_name == 'p') {
base = 16;
} else if (to_conv.conv_name == 'd') {
base = 10;
Expand Down Expand Up @@ -122,7 +123,7 @@ int convert_int(Reader *reader, const FormatSection &to_conv) {
return READ_OK;
}

if (to_lower(cur_char) == 'x') {
if (internal::tolower(cur_char) == 'x') {
// This is a valid hex prefix.

is_number = false;
Expand Down Expand Up @@ -175,17 +176,18 @@ int convert_int(Reader *reader, const FormatSection &to_conv) {

const uintmax_t max_div_by_base = MAX / base;

if (internal::isalnum(cur_char) && b36_char_to_int(cur_char) < base) {
if (internal::isalnum(cur_char) &&
internal::b36_char_to_int(cur_char) < base) {
is_number = true;
}

bool has_overflow = false;
size_t i = 0;
for (; i < max_width && internal::isalnum(cur_char) &&
b36_char_to_int(cur_char) < base;
internal::b36_char_to_int(cur_char) < base;
++i, cur_char = reader->getc()) {

uintmax_t cur_digit = b36_char_to_int(cur_char);
uintmax_t cur_digit = internal::b36_char_to_int(cur_char);

if (result == MAX) {
has_overflow = true;
Expand Down
4 changes: 3 additions & 1 deletion libc/src/stdio/scanf_core/ptr_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "src/stdio/scanf_core/ptr_converter.h"

#include "src/__support/ctype_utils.h"
#include "src/__support/macros/config.h"
#include "src/stdio/scanf_core/converter_utils.h"
#include "src/stdio/scanf_core/core_structs.h"
Expand All @@ -24,7 +25,8 @@ int convert_pointer(Reader *reader, const FormatSection &to_conv) {
// Check if it's exactly the nullptr string, if so then it's a nullptr.
char cur_char = reader->getc();
size_t i = 0;
for (; i < sizeof(nullptr_string) && to_lower(cur_char) == nullptr_string[i];
for (; i < (sizeof(nullptr_string) - 1) &&
internal::tolower(cur_char) == nullptr_string[i];
++i) {
cur_char = reader->getc();
}
Expand Down
4 changes: 3 additions & 1 deletion libc/test/UnitTest/MemoryMatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "MemoryMatcher.h"

#include "src/__support/ctype_utils.h"
#include "src/__support/macros/config.h"
#include "test/UnitTest/Test.h"

Expand Down Expand Up @@ -40,7 +41,8 @@ bool MemoryMatcher::match(MemoryView actualValue) {

static void display(char C) {
const auto print = [](unsigned char I) {
tlog << static_cast<char>(I < 10 ? '0' + I : 'A' + I - 10);
tlog << static_cast<char>(LIBC_NAMESPACE::internal::toupper(
LIBC_NAMESPACE::internal::int_to_b36_char(I)));
};
print(static_cast<unsigned char>(C) / 16);
print(static_cast<unsigned char>(C) & 15);
Expand Down
5 changes: 3 additions & 2 deletions libc/test/src/__support/CPP/stringview_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ TEST(LlvmLibcStringViewTest, Observer) {
ASSERT_EQ(ABC.back(), 'c');
}

bool isDigit(char c) { return c >= '0' && c <= '9'; }

TEST(LlvmLibcStringViewTest, FindFirstOf) {
string_view Tmp("abca");
ASSERT_TRUE(Tmp.find_first_of('a') == 0);
Expand Down Expand Up @@ -236,6 +234,9 @@ TEST(LlvmLibcStringViewTest, FindFirstNotOf) {

TEST(LlvmLibcStringViewTest, Contains) {
string_view Empty;
static_assert(
'a' < 'z',
"This test only supports character encodings where 'a' is below 'z'");
for (char c = 'a'; c < 'z'; ++c)
EXPECT_FALSE(Empty.contains(c));

Expand Down
37 changes: 32 additions & 5 deletions libc/test/src/ctype/isalnum_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,45 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "src/ctype/isalnum.h"

#include "test/UnitTest/Test.h"

TEST(LlvmLibcIsAlNum, SimpleTest) {
EXPECT_NE(LIBC_NAMESPACE::isalnum('a'), 0);
EXPECT_NE(LIBC_NAMESPACE::isalnum('B'), 0);
EXPECT_NE(LIBC_NAMESPACE::isalnum('3'), 0);

EXPECT_EQ(LIBC_NAMESPACE::isalnum(' '), 0);
EXPECT_EQ(LIBC_NAMESPACE::isalnum('?'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isalnum('\0'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isalnum(-1), 0);
}

// TODO: Merge the ctype tests using this framework.
constexpr char ALNUM_ARRAY[] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
};

bool in_span(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) {
for (size_t i = 0; i < arr.size(); ++i)
if (static_cast<int>(arr[i]) == ch)
return true;
return false;
}

TEST(LlvmLibcIsAlNum, DefaultLocale) {
// Loops through all characters, verifying that numbers and letters
// return non-zero integer and everything else returns a zero.
for (int c = -255; c < 255; ++c) {
if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
('0' <= c && c <= '9'))
EXPECT_NE(LIBC_NAMESPACE::isalnum(c), 0);
for (int ch = -255; ch < 255; ++ch) {
if (in_span(ch, ALNUM_ARRAY))
EXPECT_NE(LIBC_NAMESPACE::isalnum(ch), 0);
else
EXPECT_EQ(LIBC_NAMESPACE::isalnum(c), 0);
EXPECT_EQ(LIBC_NAMESPACE::isalnum(ch), 0);
}
}
30 changes: 29 additions & 1 deletion libc/test/src/ctype/isalpha_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,43 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "src/ctype/isalpha.h"

#include "test/UnitTest/Test.h"

TEST(LlvmLibcIsAlpha, SimpleTest) {
EXPECT_NE(LIBC_NAMESPACE::isalpha('a'), 0);
EXPECT_NE(LIBC_NAMESPACE::isalpha('B'), 0);

EXPECT_EQ(LIBC_NAMESPACE::isalpha('3'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isalpha(' '), 0);
EXPECT_EQ(LIBC_NAMESPACE::isalpha('?'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isalpha('\0'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isalpha(-1), 0);
}

// TODO: Merge the ctype tests using this framework.
constexpr char ALPHA_ARRAY[] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
};

bool in_span(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) {
for (size_t i = 0; i < arr.size(); ++i)
if (static_cast<int>(arr[i]) == ch)
return true;
return false;
}

TEST(LlvmLibcIsAlpha, DefaultLocale) {
// Loops through all characters, verifying that letters return a
// non-zero integer and everything else returns zero.
// TODO: encoding indep
for (int ch = -255; ch < 255; ++ch) {
if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'))
if (in_span(ch, ALPHA_ARRAY))
EXPECT_NE(LIBC_NAMESPACE::isalpha(ch), 0);
else
EXPECT_EQ(LIBC_NAMESPACE::isalpha(ch), 0);
Expand Down
30 changes: 27 additions & 3 deletions libc/test/src/ctype/isdigit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,39 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "src/ctype/isdigit.h"

#include "test/UnitTest/Test.h"

TEST(LlvmLibcIsDigit, SimpleTest) {
EXPECT_NE(LIBC_NAMESPACE::isdigit('3'), 0);

EXPECT_EQ(LIBC_NAMESPACE::isdigit('a'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isdigit('B'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isdigit(' '), 0);
EXPECT_EQ(LIBC_NAMESPACE::isdigit('?'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isdigit('\0'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isdigit(-1), 0);
}

// TODO: Merge the ctype tests using this framework.
constexpr char DIGIT_ARRAY[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
};

bool in_span(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) {
for (size_t i = 0; i < arr.size(); ++i)
if (static_cast<int>(arr[i]) == ch)
return true;
return false;
}

TEST(LlvmLibcIsDigit, DefaultLocale) {
// Loops through all characters, verifying that numbers return a
// non-zero integer and everything else returns zero.
// Loops through all characters, verifying that numbers and letters
// return non-zero integer and everything else returns a zero.
for (int ch = -255; ch < 255; ++ch) {
if ('0' <= ch && ch <= '9')
if (in_span(ch, DIGIT_ARRAY))
EXPECT_NE(LIBC_NAMESPACE::isdigit(ch), 0);
else
EXPECT_EQ(LIBC_NAMESPACE::isdigit(ch), 0);
Expand Down
32 changes: 29 additions & 3 deletions libc/test/src/ctype/islower_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,40 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "src/ctype/islower.h"

#include "test/UnitTest/Test.h"

TEST(LlvmLibcIsLower, SimpleTest) {
EXPECT_NE(LIBC_NAMESPACE::islower('a'), 0);

EXPECT_EQ(LIBC_NAMESPACE::islower('B'), 0);
EXPECT_EQ(LIBC_NAMESPACE::islower('3'), 0);
EXPECT_EQ(LIBC_NAMESPACE::islower(' '), 0);
EXPECT_EQ(LIBC_NAMESPACE::islower('?'), 0);
EXPECT_EQ(LIBC_NAMESPACE::islower('\0'), 0);
EXPECT_EQ(LIBC_NAMESPACE::islower(-1), 0);
}

// TODO: Merge the ctype tests using this framework.
constexpr char LOWER_ARRAY[] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
};

bool in_span(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) {
for (size_t i = 0; i < arr.size(); ++i)
if (static_cast<int>(arr[i]) == ch)
return true;
return false;
}

TEST(LlvmLibcIsLower, DefaultLocale) {
// Loops through all characters, verifying that lowercase letters
// return a non-zero integer and everything else returns zero.
// Loops through all characters, verifying that numbers and letters
// return non-zero integer and everything else returns a zero.
for (int ch = -255; ch < 255; ++ch) {
if ('a' <= ch && ch <= 'z')
if (in_span(ch, LOWER_ARRAY))
EXPECT_NE(LIBC_NAMESPACE::islower(ch), 0);
else
EXPECT_EQ(LIBC_NAMESPACE::islower(ch), 0);
Expand Down
32 changes: 29 additions & 3 deletions libc/test/src/ctype/isupper_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,40 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "src/ctype/isupper.h"

#include "test/UnitTest/Test.h"

TEST(LlvmLibcIsUpper, SimpleTest) {
EXPECT_NE(LIBC_NAMESPACE::isupper('B'), 0);

EXPECT_EQ(LIBC_NAMESPACE::isupper('a'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isupper('3'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isupper(' '), 0);
EXPECT_EQ(LIBC_NAMESPACE::isupper('?'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isupper('\0'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isupper(-1), 0);
}

// TODO: Merge the ctype tests using this framework.
constexpr char UPPER_ARRAY[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
};

bool in_span(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) {
for (size_t i = 0; i < arr.size(); ++i)
if (static_cast<int>(arr[i]) == ch)
return true;
return false;
}

TEST(LlvmLibcIsUpper, DefaultLocale) {
// Loops through all characters, verifying that uppercase letters
// return a non-zero integer and everything else returns zero.
// Loops through all characters, verifying that numbers and letters
// return non-zero integer and everything else returns a zero.
for (int ch = -255; ch < 255; ++ch) {
if ('A' <= ch && ch <= 'Z')
if (in_span(ch, UPPER_ARRAY))
EXPECT_NE(LIBC_NAMESPACE::isupper(ch), 0);
else
EXPECT_EQ(LIBC_NAMESPACE::isupper(ch), 0);
Expand Down
34 changes: 31 additions & 3 deletions libc/test/src/ctype/isxdigit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,41 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "src/ctype/isxdigit.h"

#include "test/UnitTest/Test.h"

TEST(LlvmLibcIsXDigit, DefaultLocale) {
TEST(LlvmLibcIsXdigit, SimpleTest) {
EXPECT_NE(LIBC_NAMESPACE::isxdigit('a'), 0);
EXPECT_NE(LIBC_NAMESPACE::isxdigit('B'), 0);
EXPECT_NE(LIBC_NAMESPACE::isxdigit('3'), 0);

EXPECT_EQ(LIBC_NAMESPACE::isxdigit('z'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isxdigit(' '), 0);
EXPECT_EQ(LIBC_NAMESPACE::isxdigit('?'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isxdigit('\0'), 0);
EXPECT_EQ(LIBC_NAMESPACE::isxdigit(-1), 0);
}

// TODO: Merge the ctype tests using this framework.
constexpr char XDIGIT_ARRAY[] = {
'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E',
'F', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
};

bool in_span(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) {
for (size_t i = 0; i < arr.size(); ++i)
if (static_cast<int>(arr[i]) == ch)
return true;
return false;
}

TEST(LlvmLibcIsXdigit, DefaultLocale) {
// Loops through all characters, verifying that numbers and letters
// return non-zero integer and everything else returns a zero.
for (int ch = -255; ch < 255; ++ch) {
if (('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f') ||
('A' <= ch && ch <= 'F'))
if (in_span(ch, XDIGIT_ARRAY))
EXPECT_NE(LIBC_NAMESPACE::isxdigit(ch), 0);
else
EXPECT_EQ(LIBC_NAMESPACE::isxdigit(ch), 0);
Expand Down
43 changes: 40 additions & 3 deletions libc/test/src/ctype/tolower_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,51 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "src/ctype/tolower.h"

#include "test/UnitTest/Test.h"

TEST(LlvmLibcToLower, SimpleTest) {
EXPECT_EQ(LIBC_NAMESPACE::tolower('a'), int('a'));
EXPECT_EQ(LIBC_NAMESPACE::tolower('B'), int('b'));
EXPECT_EQ(LIBC_NAMESPACE::tolower('3'), int('3'));

EXPECT_EQ(LIBC_NAMESPACE::tolower(' '), int(' '));
EXPECT_EQ(LIBC_NAMESPACE::tolower('?'), int('?'));
EXPECT_EQ(LIBC_NAMESPACE::tolower('\0'), int('\0'));
EXPECT_EQ(LIBC_NAMESPACE::tolower(-1), int(-1));
}

// TODO: Merge the ctype tests using this framework.
// Invariant: UPPER_ARR and LOWER_ARR are both the complete alphabet in the same
// order.
constexpr char UPPER_ARR[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
};
constexpr char LOWER_ARR[] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
};

static_assert(
sizeof(UPPER_ARR) == sizeof(LOWER_ARR),
"There must be the same number of uppercase and lowercase letters.");

int span_index(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) {
for (size_t i = 0; i < arr.size(); ++i)
if (static_cast<int>(arr[i]) == ch)
return static_cast<int>(i);
return -1;
}

TEST(LlvmLibcToLower, DefaultLocale) {
for (int ch = -255; ch < 255; ++ch) {
// This follows pattern 'A' + 32 = 'a'.
if ('A' <= ch && ch <= 'Z')
EXPECT_EQ(LIBC_NAMESPACE::tolower(ch), ch + 32);
int char_index = span_index(ch, UPPER_ARR);
if (char_index != -1)
EXPECT_EQ(LIBC_NAMESPACE::tolower(ch),
static_cast<int>(LOWER_ARR[char_index]));
else
EXPECT_EQ(LIBC_NAMESPACE::tolower(ch), ch);
}
Expand Down
43 changes: 40 additions & 3 deletions libc/test/src/ctype/toupper_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,51 @@
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/span.h"
#include "src/ctype/toupper.h"

#include "test/UnitTest/Test.h"

TEST(LlvmLibcToUpper, SimpleTest) {
EXPECT_EQ(LIBC_NAMESPACE::toupper('a'), int('A'));
EXPECT_EQ(LIBC_NAMESPACE::toupper('B'), int('B'));
EXPECT_EQ(LIBC_NAMESPACE::toupper('3'), int('3'));

EXPECT_EQ(LIBC_NAMESPACE::toupper(' '), int(' '));
EXPECT_EQ(LIBC_NAMESPACE::toupper('?'), int('?'));
EXPECT_EQ(LIBC_NAMESPACE::toupper('\0'), int('\0'));
EXPECT_EQ(LIBC_NAMESPACE::toupper(-1), int(-1));
}

// TODO: Merge the ctype tests using this framework.
// Invariant: UPPER_ARR and LOWER_ARR are both the complete alphabet in the same
// order.
constexpr char UPPER_ARR[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
};
constexpr char LOWER_ARR[] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
};

static_assert(
sizeof(UPPER_ARR) == sizeof(LOWER_ARR),
"There must be the same number of uppercase and lowercase letters.");

int span_index(int ch, LIBC_NAMESPACE::cpp::span<const char> arr) {
for (size_t i = 0; i < arr.size(); ++i)
if (static_cast<int>(arr[i]) == ch)
return static_cast<int>(i);
return -1;
}

TEST(LlvmLibcToUpper, DefaultLocale) {
for (int ch = -255; ch < 255; ++ch) {
// This follows pattern 'a' - 32 = 'A'.
if ('a' <= ch && ch <= 'z')
EXPECT_EQ(LIBC_NAMESPACE::toupper(ch), ch - 32);
int char_index = span_index(ch, LOWER_ARR);
if (char_index != -1)
EXPECT_EQ(LIBC_NAMESPACE::toupper(ch),
static_cast<int>(UPPER_ARR[char_index]));
else
EXPECT_EQ(LIBC_NAMESPACE::toupper(ch), ch);
}
Expand Down
27 changes: 13 additions & 14 deletions libc/test/src/stdlib/StrtolTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/macros/properties/architectures.h"
#include "src/errno/libc_errno.h"
#include "test/UnitTest/Test.h"
Expand All @@ -16,14 +17,6 @@

using LIBC_NAMESPACE::cpp::is_signed_v;

static inline char int_to_b36_char(int input) {
if (input < 0 || input > 36)
return '0';
if (input < 10)
return static_cast<char>('0' + input);
return static_cast<char>('A' + input - 10);
}

template <typename ReturnT>
struct StrtoTest : public LIBC_NAMESPACE::testing::Test {
using FunctionT = ReturnT (*)(const char *, char **, int);
Expand Down Expand Up @@ -207,7 +200,8 @@ struct StrtoTest : public LIBC_NAMESPACE::testing::Test {
char small_string[4] = {'\0', '\0', '\0', '\0'};
for (int base = 2; base <= 36; ++base) {
for (int first_digit = 0; first_digit <= 36; ++first_digit) {
small_string[0] = int_to_b36_char(first_digit);
small_string[0] =
LIBC_NAMESPACE::internal::int_to_b36_char(first_digit);
if (first_digit < base) {
LIBC_NAMESPACE::libc_errno = 0;
ASSERT_EQ(func(small_string, nullptr, base),
Expand All @@ -223,9 +217,11 @@ struct StrtoTest : public LIBC_NAMESPACE::testing::Test {

for (int base = 2; base <= 36; ++base) {
for (int first_digit = 0; first_digit <= 36; ++first_digit) {
small_string[0] = int_to_b36_char(first_digit);
small_string[0] =
LIBC_NAMESPACE::internal::int_to_b36_char(first_digit);
for (int second_digit = 0; second_digit <= 36; ++second_digit) {
small_string[1] = int_to_b36_char(second_digit);
small_string[1] =
LIBC_NAMESPACE::internal::int_to_b36_char(second_digit);
if (first_digit < base && second_digit < base) {
LIBC_NAMESPACE::libc_errno = 0;
ASSERT_EQ(
Expand All @@ -248,11 +244,14 @@ struct StrtoTest : public LIBC_NAMESPACE::testing::Test {

for (int base = 2; base <= 36; ++base) {
for (int first_digit = 0; first_digit <= 36; ++first_digit) {
small_string[0] = int_to_b36_char(first_digit);
small_string[0] =
LIBC_NAMESPACE::internal::int_to_b36_char(first_digit);
for (int second_digit = 0; second_digit <= 36; ++second_digit) {
small_string[1] = int_to_b36_char(second_digit);
small_string[1] =
LIBC_NAMESPACE::internal::int_to_b36_char(second_digit);
for (int third_digit = 0; third_digit <= limit; ++third_digit) {
small_string[2] = int_to_b36_char(third_digit);
small_string[2] =
LIBC_NAMESPACE::internal::int_to_b36_char(third_digit);

if (first_digit < base && second_digit < base &&
third_digit < base) {
Expand Down
20 changes: 10 additions & 10 deletions libc/test/src/string/strcmp_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ TEST(LlvmLibcStrCmpTest, EmptyStringShouldNotEqualNonEmptyString) {
const char *s2 = "abc";
int result = LIBC_NAMESPACE::strcmp(empty, s2);
// This should be '\0' - 'a' = -97
ASSERT_EQ(result, -97);
ASSERT_EQ(result, '\0' - 'a');

// Similar case if empty string is second argument.
const char *s3 = "123";
result = LIBC_NAMESPACE::strcmp(s3, empty);
// This should be '1' - '\0' = 49
ASSERT_EQ(result, 49);
ASSERT_EQ(result, '1' - '\0');
}

TEST(LlvmLibcStrCmpTest, EqualStringsShouldReturnZero) {
Expand All @@ -50,50 +50,50 @@ TEST(LlvmLibcStrCmpTest, ShouldReturnResultOfFirstDifference) {
const char *s2 = "___C55__";
int result = LIBC_NAMESPACE::strcmp(s1, s2);
// This should return 'B' - 'C' = -1.
ASSERT_EQ(result, -1);
ASSERT_EQ(result, 'B' - 'C');

// Verify operands reversed.
result = LIBC_NAMESPACE::strcmp(s2, s1);
// This should return 'C' - 'B' = 1.
ASSERT_EQ(result, 1);
ASSERT_EQ(result, 'C' - 'B');
}

TEST(LlvmLibcStrCmpTest, CapitalizedLetterShouldNotBeEqual) {
const char *s1 = "abcd";
const char *s2 = "abCd";
int result = LIBC_NAMESPACE::strcmp(s1, s2);
// 'c' - 'C' = 32.
ASSERT_EQ(result, 32);
ASSERT_EQ(result, 'c' - 'C');

// Verify operands reversed.
result = LIBC_NAMESPACE::strcmp(s2, s1);
// 'C' - 'c' = -32.
ASSERT_EQ(result, -32);
ASSERT_EQ(result, 'C' - 'c');
}

TEST(LlvmLibcStrCmpTest, UnequalLengthStringsShouldNotReturnZero) {
const char *s1 = "abc";
const char *s2 = "abcd";
int result = LIBC_NAMESPACE::strcmp(s1, s2);
// '\0' - 'd' = -100.
ASSERT_EQ(result, -100);
ASSERT_EQ(result, -'\0' - 'd');

// Verify operands reversed.
result = LIBC_NAMESPACE::strcmp(s2, s1);
// 'd' - '\0' = 100.
ASSERT_EQ(result, 100);
ASSERT_EQ(result, 'd' - '\0');
}

TEST(LlvmLibcStrCmpTest, StringArgumentSwapChangesSign) {
const char *a = "a";
const char *b = "b";
int result = LIBC_NAMESPACE::strcmp(b, a);
// 'b' - 'a' = 1.
ASSERT_EQ(result, 1);
ASSERT_EQ(result, 'b' - 'a');

result = LIBC_NAMESPACE::strcmp(a, b);
// 'a' - 'b' = -1.
ASSERT_EQ(result, -1);
ASSERT_EQ(result, 'a' - 'b');
}

TEST(LlvmLibcStrCmpTest, Case) {
Expand Down
3 changes: 3 additions & 0 deletions utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@ libc_support_library(
hdrs = ["src/__support/integer_literals.h"],
deps = [
":__support_cpp_limits",
":__support_ctype_utils",
":__support_uint128",
],
)
Expand Down Expand Up @@ -772,6 +773,7 @@ libc_support_library(
":__support_cpp_span",
":__support_cpp_string_view",
":__support_cpp_type_traits",
":__support_ctype_utils",
],
)

Expand Down Expand Up @@ -4450,6 +4452,7 @@ libc_support_library(
":__support_cpp_limits",
":__support_cpp_span",
":__support_cpp_string_view",
":__support_ctype_utils",
":__support_float_to_string",
":__support_fputil_fenv_impl",
":__support_fputil_fp_bits",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ libc_support_library(
"//libc:__support_stringutil",
"//libc:__support_uint128",
"//libc:errno",
"//libc:llvm_libc_macros_stdfix_macros",
"//llvm:Support",
"//libc:func_aligned_alloc",
"//libc:func_free",
"//libc:func_malloc",
"//libc:func_realloc",
"//libc:llvm_libc_macros_stdfix_macros",
"//llvm:Support",
],
)

Expand Down Expand Up @@ -121,6 +121,7 @@ libc_support_library(
"//libc:__support_cpp_bitset",
"//libc:__support_cpp_span",
"//libc:__support_cpp_type_traits",
"//libc:__support_ctype_utils",
"//libc:__support_macros_config",
],
)
Expand Down