Skip to content

Commit

Permalink
[libc] Adds string and TestLogger classes, use them in LibcTest
Browse files Browse the repository at this point in the history
  • Loading branch information
gchatelet committed Apr 2, 2023
1 parent 2434c8f commit fc5ae0a
Show file tree
Hide file tree
Showing 11 changed files with 650 additions and 77 deletions.
13 changes: 13 additions & 0 deletions libc/src/__support/CPP/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ add_header_library(
libc.src.__support.common
)

add_header_library(
string
HDRS
string.h
DEPENDS
.string_view
libc.src.__support.common
libc.src.__support.integer_to_string
libc.src.string.memory_utils.memcpy_implementation
libc.src.string.memory_utils.memset_implementation
libc.src.string.string_utils
)

add_header_library(
stringstream
HDRS
Expand Down
224 changes: 224 additions & 0 deletions libc/src/__support/CPP/string.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
//===-- A simple implementation of the string class -------------*- 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_CPP_STRING_H
#define LLVM_LIBC_SRC_SUPPORT_CPP_STRING_H

#include "src/__support/CPP/string_view.h"
#include "src/__support/integer_to_string.h" // IntegerToString
#include "src/string/memory_utils/memcpy_implementations.h"
#include "src/string/memory_utils/memset_implementations.h"
#include "src/string/string_utils.h" // string_length

#include <stddef.h> // size_t
#include <stdlib.h> // malloc, free

namespace __llvm_libc {
namespace cpp {

// This class mimics std::string but does not intend to be a full fledged
// implementation. Most notably it does not provide support for character traits
// nor custom allocator.
class string {
private:
static constexpr char NULL_CHARACTER = '\0';
static constexpr char *get_empty_string() {
return const_cast<char *>(&NULL_CHARACTER);
}

char *buffer_ = get_empty_string();
size_t size_ = 0;
size_t capacity_ = 0;

void reset_no_deallocate() {
buffer_ = get_empty_string();
size_ = 0;
capacity_ = 0;
}

void set_size_and_add_null_character(size_t size) {
size_ = size;
if (size_ > 0)
buffer_[size_] = NULL_CHARACTER;
else
buffer_ = get_empty_string();
}

public:
LIBC_INLINE constexpr string() {}
LIBC_INLINE string(const string &other) { this->operator+=(other); }
LIBC_INLINE string(string &&other)
: buffer_(other.buffer_), size_(other.size_), capacity_(other.capacity_) {
other.reset_no_deallocate();
}
LIBC_INLINE string(const char *cstr, size_t count) {
resize(count);
inline_memcpy((void *)buffer_, (const void *)cstr, count);
}
LIBC_INLINE string(const char *cstr)
: string(cstr, ::__llvm_libc::internal::string_length(cstr)) {}
LIBC_INLINE string(size_t size_, char value) {
resize(size_);
inline_memset((void *)buffer_, value, size_);
}

LIBC_INLINE string &operator=(const string &other) {
resize(0);
return (*this) += other;
}

LIBC_INLINE string &operator=(string &&other) {
buffer_ = other.buffer_;
size_ = other.size_;
capacity_ = other.capacity_;
other.reset_no_deallocate();
return *this;
}

LIBC_INLINE ~string() {
if (buffer_ != get_empty_string())
::free(buffer_);
}

LIBC_INLINE size_t capacity() const { return capacity_; }
LIBC_INLINE size_t size() const { return size_; }
LIBC_INLINE bool empty() const { return size_ == 0; }

LIBC_INLINE const char *data() const { return buffer_; }
LIBC_INLINE char *data() { return buffer_; }

LIBC_INLINE const char *begin() const { return data(); }
LIBC_INLINE char *begin() { return data(); }

LIBC_INLINE const char *end() const { return data() + size_; }
LIBC_INLINE char *end() { return data() + size_; }

LIBC_INLINE const char &front() const { return data()[0]; }
LIBC_INLINE char &front() { return data()[0]; }

LIBC_INLINE const char &back() const { return data()[size_ - 1]; }
LIBC_INLINE char &back() { return data()[size_ - 1]; }

LIBC_INLINE constexpr const char &operator[](size_t index) const {
return data()[index];
}
LIBC_INLINE char &operator[](size_t index) { return data()[index]; }

LIBC_INLINE const char *c_str() const { return data(); }

LIBC_INLINE operator string_view() const {
return string_view(buffer_, size_);
}

LIBC_INLINE void reserve(size_t new_capacity) {
++new_capacity; // Accounting for the terminating '\0'
if (new_capacity <= capacity_)
return;
// We extend the capacity to amortize buffer_ reallocations.
// We choose to augment the value by 11 / 8, this is about +40% and division
// by 8 is cheap. We guard the extension so the operation doesn't overflow.
if (new_capacity < SIZE_MAX / 11)
new_capacity = new_capacity * 11 / 8;
if (void *Ptr = ::realloc(empty() ? nullptr : buffer_, new_capacity)) {
buffer_ = static_cast<char *>(Ptr);
} else {
__builtin_unreachable(); // out of memory
}
capacity_ = new_capacity;
}

LIBC_INLINE void resize(size_t size) {
if (size > capacity_)
reserve(size);
set_size_and_add_null_character(size);
}

LIBC_INLINE string &operator+=(const string &rhs) {
const size_t new_size = size_ + rhs.size();
reserve(new_size);
inline_memcpy(buffer_ + size_, rhs.data(), rhs.size());
set_size_and_add_null_character(new_size);
return *this;
}

LIBC_INLINE string &operator+=(const char c) {
const size_t new_size = size_ + 1;
reserve(new_size);
buffer_[size_] = c;
set_size_and_add_null_character(new_size);
return *this;
}
};

LIBC_INLINE bool operator==(const string &lhs, const string &rhs) {
return string_view(lhs) == string_view(rhs);
}
LIBC_INLINE bool operator!=(const string &lhs, const string &rhs) {
return string_view(lhs) != string_view(rhs);
}
LIBC_INLINE bool operator<(const string &lhs, const string &rhs) {
return string_view(lhs) < string_view(rhs);
}
LIBC_INLINE bool operator<=(const string &lhs, const string &rhs) {
return string_view(lhs) <= string_view(rhs);
}
LIBC_INLINE bool operator>(const string &lhs, const string &rhs) {
return string_view(lhs) > string_view(rhs);
}
LIBC_INLINE bool operator>=(const string &lhs, const string &rhs) {
return string_view(lhs) >= string_view(rhs);
}

LIBC_INLINE string operator+(const string &lhs, const string &rhs) {
string Tmp(lhs);
return Tmp += rhs;
}
LIBC_INLINE string operator+(const string &lhs, const char *rhs) {
return lhs + string(rhs);
}
LIBC_INLINE string operator+(const char *lhs, const string &rhs) {
return string(lhs) + rhs;
}

namespace internal {
template <typename T> string to_dec_string(T value) {
char dec_buf[IntegerToString::dec_bufsize<T>()];
auto maybe_string_view = IntegerToString::dec(value, dec_buf);
const auto &string_view = *maybe_string_view;
return string(string_view.data(), string_view.size());
}
} // namespace internal

LIBC_INLINE string to_string(int value) {
return internal::to_dec_string<int>(value);
}
LIBC_INLINE string to_string(long value) {
return internal::to_dec_string<long>(value);
}
LIBC_INLINE string to_string(long long value) {
return internal::to_dec_string<long long>(value);
}
LIBC_INLINE string to_string(unsigned value) {
return internal::to_dec_string<unsigned>(value);
}
LIBC_INLINE string to_string(unsigned long value) {
return internal::to_dec_string<unsigned long>(value);
}
LIBC_INLINE string to_string(unsigned long long value) {
return internal::to_dec_string<unsigned long long>(value);
}

// TODO: Support floating point
// LIBC_INLINE string to_string(float value);
// LIBC_INLINE string to_string(double value);
// LIBC_INLINE string to_string(long double value);

} // namespace cpp
} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_SUPPORT_CPP_STRING_H
43 changes: 30 additions & 13 deletions libc/test/UnitTest/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
add_library(
TestLogger
TestLogger.cpp
TestLogger.h
)
target_include_directories(TestLogger PUBLIC ${LIBC_SOURCE_DIR})
add_dependencies(TestLogger
libc.src.__support.CPP.string
libc.src.__support.CPP.string_view
libc.src.__support.OSUtil.osutil
)

add_library(
LibcUnitTest
Test.h
LibcTest.cpp
LibcTest.h
)
target_include_directories(LibcUnitTest PUBLIC ${LIBC_SOURCE_DIR})
add_dependencies(LibcUnitTest libc.src.__support.CPP.type_traits libc.src.__support.uint128)
target_link_libraries(LibcUnitTest PUBLIC libc_test_utils)
add_dependencies(
LibcUnitTest
libc.src.__support.CPP.string
libc.src.__support.CPP.string_view
libc.src.__support.CPP.type_traits
libc.src.__support.uint128 TestLogger)
target_link_libraries(LibcUnitTest PUBLIC libc_test_utils TestLogger)

add_library(
LibcUnitTestMain
Expand All @@ -20,17 +37,17 @@ target_link_libraries(LibcUnitTestMain PUBLIC LibcUnitTest libc_test_utils)
add_header_library(
string_utils
HDRS
StringUtils.h
StringUtils.h
DEPENDS
libc.src.__support.CPP.type_traits
)

add_library(
LibcFPTestHelpers
FPExceptMatcher.cpp
FPExceptMatcher.h
FPMatcher.cpp
FPMatcher.h
FPExceptMatcher.cpp
FPExceptMatcher.h
FPMatcher.cpp
FPMatcher.h
)
target_include_directories(LibcFPTestHelpers PUBLIC ${LIBC_SOURCE_DIR})
target_link_libraries(LibcFPTestHelpers LibcUnitTest libc_test_utils)
Expand All @@ -44,8 +61,8 @@ add_dependencies(

add_library(
LibcMemoryHelpers
MemoryMatcher.h
MemoryMatcher.cpp
MemoryMatcher.h
MemoryMatcher.cpp
)
target_include_directories(LibcMemoryHelpers PUBLIC ${LIBC_SOURCE_DIR})
target_link_libraries(LibcMemoryHelpers LibcUnitTest)
Expand All @@ -57,8 +74,8 @@ add_dependencies(

add_library(
LibcPrintfHelpers
PrintfMatcher.h
PrintfMatcher.cpp
PrintfMatcher.h
PrintfMatcher.cpp
)
target_include_directories(LibcPrintfHelpers PUBLIC ${LIBC_SOURCE_DIR})
target_link_libraries(LibcPrintfHelpers LibcUnitTest)
Expand All @@ -72,8 +89,8 @@ add_dependencies(

add_library(
LibcScanfHelpers
ScanfMatcher.h
ScanfMatcher.cpp
ScanfMatcher.h
ScanfMatcher.cpp
)
target_include_directories(LibcScanfHelpers PUBLIC ${LIBC_SOURCE_DIR})
target_link_libraries(LibcScanfHelpers LibcUnitTest)
Expand Down

0 comments on commit fc5ae0a

Please sign in to comment.