Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libc/src/__support/wchar/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_object_library(
SRCS
wcrtomb.cpp
DEPENDS
libc.hdr.errno_macros
libc.hdr.types.char32_t
libc.hdr.types.size_t
libc.hdr.types.wchar_t
Expand All @@ -43,6 +44,7 @@ add_object_library(
SRCS
mbrtowc.cpp
DEPENDS
libc.hdr.errno_macros
libc.hdr.types.wchar_t
libc.hdr.types.size_t
libc.src.__support.common
Expand Down
5 changes: 4 additions & 1 deletion libc/src/__support/wchar/mbrtowc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "src/__support/wchar/mbrtowc.h"
#include "hdr/errno_macros.h"
#include "hdr/types/mbstate_t.h"
#include "hdr/types/size_t.h"
#include "hdr/types/wchar_t.h"
Expand All @@ -22,6 +23,8 @@ namespace internal {
ErrorOr<size_t> mbrtowc(wchar_t *__restrict pwc, const char *__restrict s,
size_t n, mbstate *__restrict ps) {
CharacterConverter char_conv(ps);
if (!char_conv.isValidState())
return Error(EINVAL);
if (s == nullptr)
return 0;
size_t i = 0;
Expand All @@ -30,7 +33,7 @@ ErrorOr<size_t> mbrtowc(wchar_t *__restrict pwc, const char *__restrict s,
int err = char_conv.push(static_cast<char8_t>(s[i]));
// Encoding error
if (err == -1)
return Error(-1);
return Error(EILSEQ);
}
auto wc = char_conv.pop_utf32();
if (wc.has_value()) {
Expand Down
8 changes: 6 additions & 2 deletions libc/src/__support/wchar/wcrtomb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "src/__support/wchar/character_converter.h"
#include "src/__support/wchar/mbstate.h"

#include "hdr/errno_macros.h"
#include "hdr/types/char32_t.h"
#include "hdr/types/size_t.h"
#include "hdr/types/wchar_t.h"
Expand All @@ -26,12 +27,15 @@ ErrorOr<size_t> wcrtomb(char *__restrict s, wchar_t wc,

CharacterConverter cr(ps);

if (!cr.isValidState())
return Error(EINVAL);

if (s == nullptr)
return Error(-1);
return Error(EILSEQ);

int status = cr.push(static_cast<char32_t>(wc));
if (status != 0)
return Error(status);
return Error(EILSEQ);

size_t count = 0;
while (!cr.isEmpty()) {
Expand Down
2 changes: 1 addition & 1 deletion libc/src/wchar/mbrtowc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ LLVM_LIBC_FUNCTION(size_t, mbrtowc,
: reinterpret_cast<internal::mbstate *>(ps));
if (!ret.has_value()) {
// Encoding failure
libc_errno = EILSEQ;
libc_errno = ret.error();
return -1;
}
return ret.value();
Expand Down
2 changes: 1 addition & 1 deletion libc/src/wchar/wcrtomb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ LLVM_LIBC_FUNCTION(size_t, wcrtomb,
: reinterpret_cast<internal::mbstate *>(ps));

if (!result.has_value()) {
libc_errno = EILSEQ;
libc_errno = result.error();
return -1;
}

Expand Down
4 changes: 4 additions & 0 deletions libc/test/src/wchar/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ add_libc_test(
mbrtowc_test.cpp
DEPENDS
libc.src.__support.libc_errno
libc.src.__support.wchar.mbstate
libc.src.string.memset
libc.src.wchar.mbrtowc
libc.hdr.types.mbstate_t
libc.hdr.types.wchar_t
libc.test.UnitTest.ErrnoCheckingTest
)

add_libc_test(
Expand Down Expand Up @@ -72,6 +74,8 @@ add_libc_test(
libc.hdr.types.wchar_t
libc.hdr.types.mbstate_t
libc.src.__support.libc_errno
libc.src.__support.wchar.mbstate
libc.test.UnitTest.ErrnoCheckingTest
)

add_libc_test(
Expand Down
63 changes: 48 additions & 15 deletions libc/test/src/wchar/mbrtowc_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,31 @@
#include "hdr/types/mbstate_t.h"
#include "hdr/types/wchar_t.h"
#include "src/__support/libc_errno.h"
#include "src/__support/wchar/mbstate.h"
#include "src/string/memset.h"
#include "src/wchar/mbrtowc.h"
#include "test/UnitTest/ErrnoCheckingTest.h"
#include "test/UnitTest/Test.h"

TEST(LlvmLibcMBRToWC, OneByte) {
using LlvmLibcMBRToWCTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;

TEST_F(LlvmLibcMBRToWCTest, OneByte) {
const char *ch = "A";
wchar_t dest[2];
// Testing if it works with nullptr mbstate_t
mbstate_t *mb = nullptr;
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 1, mb);
ASSERT_EQ(static_cast<char>(*dest), 'A');
ASSERT_EQ(static_cast<int>(n), 1);
ASSERT_ERRNO_SUCCESS();

// Should fail since we have not read enough
n = LIBC_NAMESPACE::mbrtowc(dest, ch, 0, mb);
ASSERT_EQ(static_cast<int>(n), -2);
ASSERT_ERRNO_SUCCESS();
}

TEST(LlvmLibcMBRToWC, TwoByte) {
TEST_F(LlvmLibcMBRToWCTest, TwoByte) {
const char ch[2] = {static_cast<char>(0xC2),
static_cast<char>(0x8E)}; // Ž car symbol
wchar_t dest[2];
Expand All @@ -36,6 +42,7 @@ TEST(LlvmLibcMBRToWC, TwoByte) {
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 2, mb);
ASSERT_EQ(static_cast<int>(*dest), 142);
ASSERT_EQ(static_cast<int>(n), 2);
ASSERT_ERRNO_SUCCESS();

// Should fail since we have not read enough
n = LIBC_NAMESPACE::mbrtowc(dest, ch, 1, mb);
Expand All @@ -44,9 +51,10 @@ TEST(LlvmLibcMBRToWC, TwoByte) {
n = LIBC_NAMESPACE::mbrtowc(dest, ch + 1, 1, mb);
ASSERT_EQ(static_cast<int>(n), 1);
ASSERT_EQ(static_cast<int>(*dest), 142);
ASSERT_ERRNO_SUCCESS();
}

TEST(LlvmLibcMBRToWC, ThreeByte) {
TEST_F(LlvmLibcMBRToWCTest, ThreeByte) {
const char ch[3] = {static_cast<char>(0xE2), static_cast<char>(0x88),
static_cast<char>(0x91)}; // ∑ sigma symbol
wchar_t dest[2];
Expand All @@ -55,17 +63,20 @@ TEST(LlvmLibcMBRToWC, ThreeByte) {
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 3, mb);
ASSERT_EQ(static_cast<int>(*dest), 8721);
ASSERT_EQ(static_cast<int>(n), 3);
ASSERT_ERRNO_SUCCESS();

// Should fail since we have not read enough
n = LIBC_NAMESPACE::mbrtowc(dest, ch, 1, mb);
ASSERT_EQ(static_cast<int>(n), -2);
ASSERT_ERRNO_SUCCESS();
// Should pass after reading two more bytes
n = LIBC_NAMESPACE::mbrtowc(dest, ch + 1, 2, mb);
ASSERT_EQ(static_cast<int>(n), 2);
ASSERT_EQ(static_cast<int>(*dest), 8721);
ASSERT_ERRNO_SUCCESS();
}

TEST(LlvmLibcMBRToWC, FourByte) {
TEST_F(LlvmLibcMBRToWCTest, FourByte) {
const char ch[4] = {static_cast<char>(0xF0), static_cast<char>(0x9F),
static_cast<char>(0xA4),
static_cast<char>(0xA1)}; // 🤡 clown emoji
Expand All @@ -75,27 +86,29 @@ TEST(LlvmLibcMBRToWC, FourByte) {
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 4, mb);
ASSERT_EQ(static_cast<int>(*dest), 129313);
ASSERT_EQ(static_cast<int>(n), 4);

ASSERT_ERRNO_SUCCESS();
// Should fail since we have not read enough
n = LIBC_NAMESPACE::mbrtowc(dest, ch, 2, mb);
ASSERT_EQ(static_cast<int>(n), -2);
ASSERT_ERRNO_SUCCESS();
// Should pass after reading two more bytes
n = LIBC_NAMESPACE::mbrtowc(dest, ch + 2, 2, mb);
ASSERT_EQ(static_cast<int>(n), 2);
ASSERT_EQ(static_cast<int>(*dest), 129313);
ASSERT_ERRNO_SUCCESS();
}

TEST(LlvmLibcMBRToWC, InvalidByte) {
TEST_F(LlvmLibcMBRToWCTest, InvalidByte) {
const char ch[1] = {static_cast<char>(0x80)};
wchar_t dest[2];
mbstate_t *mb;
LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 1, mb);
ASSERT_EQ(static_cast<int>(n), -1);
ASSERT_EQ(static_cast<int>(libc_errno), EILSEQ);
ASSERT_ERRNO_EQ(EILSEQ);
}

TEST(LlvmLibcMBRToWC, InvalidMultiByte) {
TEST_F(LlvmLibcMBRToWCTest, InvalidMultiByte) {
const char ch[4] = {static_cast<char>(0x80), static_cast<char>(0x00),
static_cast<char>(0x80),
static_cast<char>(0x00)}; // invalid sequence of bytes
Expand All @@ -105,18 +118,19 @@ TEST(LlvmLibcMBRToWC, InvalidMultiByte) {
// Trying to push all 4 should error
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 4, mb);
ASSERT_EQ(static_cast<int>(n), -1);
ASSERT_EQ(static_cast<int>(libc_errno), EILSEQ);
ASSERT_ERRNO_EQ(EILSEQ);
// Trying to push just the first one should error
n = LIBC_NAMESPACE::mbrtowc(dest, ch, 1, mb);
ASSERT_EQ(static_cast<int>(n), -1);
ASSERT_EQ(static_cast<int>(libc_errno), EILSEQ);
ASSERT_ERRNO_EQ(EILSEQ);
// Trying to push the second and third should correspond to null wc
n = LIBC_NAMESPACE::mbrtowc(dest, ch + 1, 2, mb);
ASSERT_EQ(static_cast<int>(n), 0);
ASSERT_TRUE(*dest == L'\0');
ASSERT_ERRNO_SUCCESS();
}

TEST(LlvmLibcMBRToWC, InvalidLastByte) {
TEST_F(LlvmLibcMBRToWCTest, InvalidLastByte) {
// Last byte is invalid since it does not have correct starting sequence.
// 0xC0 --> 11000000 starting sequence should be 10xxxxxx
const char ch[4] = {static_cast<char>(0xF1), static_cast<char>(0x80),
Expand All @@ -127,10 +141,10 @@ TEST(LlvmLibcMBRToWC, InvalidLastByte) {
// Trying to push all 4 should error
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 4, mb);
ASSERT_EQ(static_cast<int>(n), -1);
ASSERT_EQ(static_cast<int>(libc_errno), EILSEQ);
ASSERT_ERRNO_EQ(EILSEQ);
}

TEST(LlvmLibcMBRToWC, ValidTwoByteWithExtraRead) {
TEST_F(LlvmLibcMBRToWCTest, ValidTwoByteWithExtraRead) {
const char ch[3] = {static_cast<char>(0xC2), static_cast<char>(0x8E),
static_cast<char>(0x80)};
wchar_t dest[2];
Expand All @@ -140,9 +154,10 @@ TEST(LlvmLibcMBRToWC, ValidTwoByteWithExtraRead) {
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 3, mb);
ASSERT_EQ(static_cast<int>(n), 2);
ASSERT_EQ(static_cast<int>(*dest), 142);
ASSERT_ERRNO_SUCCESS();
}

TEST(LlvmLibcMBRToWC, TwoValidTwoBytes) {
TEST_F(LlvmLibcMBRToWCTest, TwoValidTwoBytes) {
const char ch[4] = {static_cast<char>(0xC2), static_cast<char>(0x8E),
static_cast<char>(0xC7), static_cast<char>(0x8C)};
wchar_t dest[2];
Expand All @@ -152,21 +167,39 @@ TEST(LlvmLibcMBRToWC, TwoValidTwoBytes) {
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 2, mb);
ASSERT_EQ(static_cast<int>(n), 2);
ASSERT_EQ(static_cast<int>(*dest), 142);
ASSERT_ERRNO_SUCCESS();
n = LIBC_NAMESPACE::mbrtowc(dest + 1, ch + 2, 2, mb);
ASSERT_EQ(static_cast<int>(n), 2);
ASSERT_EQ(static_cast<int>(*(dest + 1)), 460);
ASSERT_ERRNO_SUCCESS();
}

TEST(LlvmLibcMBRToWC, NullString) {
TEST_F(LlvmLibcMBRToWCTest, NullString) {
wchar_t dest[2] = {L'O', L'K'};
mbstate_t *mb;
LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
// reading on nullptr should return 0
size_t n = LIBC_NAMESPACE::mbrtowc(dest, nullptr, 2, mb);
ASSERT_EQ(static_cast<int>(n), 0);
ASSERT_TRUE(dest[0] == L'O');
ASSERT_ERRNO_SUCCESS();
// reading a null terminator should return 0
const char *ch = "\0";
n = LIBC_NAMESPACE::mbrtowc(dest, ch, 1, mb);
ASSERT_EQ(static_cast<int>(n), 0);
ASSERT_ERRNO_SUCCESS();
}

TEST_F(LlvmLibcMBRToWCTest, InvalidMBState) {
const char ch[4] = {static_cast<char>(0xC2), static_cast<char>(0x8E),
static_cast<char>(0xC7), static_cast<char>(0x8C)};
wchar_t dest[2] = {L'O', L'K'};
mbstate_t *mb;
LIBC_NAMESPACE::internal::mbstate inv;
inv.total_bytes = 6;
mb = reinterpret_cast<mbstate_t *>(&inv);
// invalid mbstate should error
size_t n = LIBC_NAMESPACE::mbrtowc(dest, ch, 2, mb);
ASSERT_EQ(static_cast<int>(n), -1);
ASSERT_ERRNO_EQ(EINVAL);
}
Loading
Loading