diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt --- a/libc/src/__support/CPP/CMakeLists.txt +++ b/libc/src/__support/CPP/CMakeLists.txt @@ -9,3 +9,11 @@ TypeTraits.h Limits.h ) + +add_header_library( + cpp_with_malloc + HDRS + vector.h + DEPENDS + libc.include.stdlib +) diff --git a/libc/src/__support/CPP/vector.h b/libc/src/__support/CPP/vector.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/CPP/vector.h @@ -0,0 +1,128 @@ +//===-- A self contained equivalent of std::vector --------------*- 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_VECTOR_H +#define LLVM_LIBC_SRC_SUPPORT_CPP_VECTOR_H + +#include +#include // For size_t. + +#include // For malloc/realloc/free + +namespace __llvm_libc { +namespace cpp { + +void simple_memset(void *dst, char value, size_t count) { + char *data_array = static_cast(dst); + for (size_t i = 0; i < count; ++i) + data_array[i] = value; +} + +// This implementation does not have a templated allocator since that feature +// isn't relevant for a libc setting. + +// Vector is a templated dynamically resizable array. This implementation is +// only meant for primitives or structs, and will not call destructors on held +// objects. +template class vector { + T *data_array; + size_t array_size; + size_t num_elements = 0; + static constexpr size_t DEFAULT_SIZE = 16; + static constexpr size_t GROWTH_FACTOR = 2; + static constexpr size_t MAX_SIZE = __SIZE_MAX__; + +public: + constexpr vector() : array_size{DEFAULT_SIZE} { + data_array = static_cast(malloc(DEFAULT_SIZE * sizeof(T))); + } + constexpr vector(size_t count) : array_size{count} { + data_array = static_cast(malloc(count * sizeof(T))); + } + constexpr vector(const vector &other) { + array_size = other.capacity(); + data_array = static_cast(malloc(array_size * sizeof(T))); + num_elements = other.size(); + for (size_t i = 0; i < num_elements; ++i) + data_array[i] = other.data()[i]; + } + + ~vector() { free(data_array); } + + constexpr vector &operator=(vector &other) { + if (this == &other) + return *this; + + array_size = other.capacity(); + data_array = static_cast(malloc(array_size * sizeof(T))); + num_elements = other.size(); + for (size_t i = 0; i < num_elements; ++i) + data_array[i] = other.data()[i]; + return *this; + } + + // can only increase the size of the array + constexpr void reserve(size_t new_cap) { + if (new_cap < array_size || new_cap > MAX_SIZE) + return; + array_size = new_cap; + realloc_array(); + } + + constexpr void shrink_to_fit() { + array_size = num_elements; + realloc_array(); + } + + constexpr void push_back(const T &value) { + if (num_elements >= array_size) + increase_size(num_elements + 1); + data_array[num_elements] = value; + ++num_elements; + } + + constexpr T &operator[](size_t pos) { + if (pos >= num_elements) + num_elements = pos + 1; + return data_array[pos]; + } + constexpr T *data() { return data_array; } + constexpr const T *data() const { return data_array; } + + constexpr bool empty() const { return num_elements == 0; } + + constexpr size_t size() const { return num_elements; } + constexpr size_t max_size() const { return MAX_SIZE; } + + constexpr size_t capacity() const { return array_size; } + + // new_size is treated as the minimum size for the new array. This function + // will increase array_size by GROWTH_FACTOR until there is space for new_size + // items. + constexpr void increase_size(size_t new_size) { + size_t temp_size = array_size; + if (temp_size == 0) + ++temp_size; + while (temp_size < new_size) + temp_size = temp_size * GROWTH_FACTOR; + if (temp_size >= MAX_SIZE) + return; + array_size = temp_size; + realloc_array(); + } + +private: + // calls realloc on data_array so that its size matches array_size + constexpr void realloc_array() { + data_array = static_cast(realloc(data_array, array_size * sizeof(T))); + } +}; +} // namespace cpp +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SUPPORT_CPP_VECTOR_H diff --git a/libc/test/utils/CPP/CMakeLists.txt b/libc/test/utils/CPP/CMakeLists.txt --- a/libc/test/utils/CPP/CMakeLists.txt +++ b/libc/test/utils/CPP/CMakeLists.txt @@ -39,3 +39,13 @@ DEPENDS libc.src.__support.CPP.standalone_cpp ) + +add_libc_unittest( + vector_test + SUITE + libc_cpp_utils_unittests + SRCS + vector_test.cpp + DEPENDS + libc.src.__support.CPP.cpp_with_malloc +) diff --git a/libc/test/utils/CPP/vector_test.cpp b/libc/test/utils/CPP/vector_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/utils/CPP/vector_test.cpp @@ -0,0 +1,53 @@ +//===-- Unittests for vector ----------------------------------------------===// +// +// 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/__support/CPP/vector.h" +#include "utils/UnitTest/Test.h" + +TEST(LlvmLibcVectorTest, SimpleConstructors) { + __llvm_libc::cpp::vector vec; + + __llvm_libc::cpp::vector vec2 = __llvm_libc::cpp::vector(10); + + __llvm_libc::cpp::vector vec3 = __llvm_libc::cpp::vector(vec2); +} + +TEST(LlvmLibcVectorTest, OrderedWriteOrderedReadTest) { + __llvm_libc::cpp::vector vec; + + for (size_t i = 0; i < 100; i = i + 2) { + vec.push_back(i); + } + ASSERT_EQ(vec.size(), size_t(50)); + ASSERT_GE(vec.capacity(), vec.size()); + for (size_t j = 0; j < vec.size(); ++j) { + ASSERT_EQ(vec[j], j * 2); + } +} + +TEST(LlvmLibcVectorTest, RandomWriteOrderedReadTest) { + __llvm_libc::cpp::vector vec = __llvm_libc::cpp::vector(0); + + ASSERT_EQ(vec.capacity(), size_t(0)); + + size_t index_array[] = {5, 2, 7, 4, 8, 9, 1, 3, 6, 0}; + for (size_t i = 0; i < 10; ++i) { + if (vec.capacity() < index_array[i]) { + vec.increase_size(index_array[i]); + ASSERT_GE(vec.capacity(), index_array[i]); + } + + vec[index_array[i]] = ((index_array[i] % 3) == 0); + } + + ASSERT_GE(vec.capacity(), size_t(10)); + + for (size_t j = 0; j < 10; ++j) { + ASSERT_EQ(vec[j], (j % 3) == 0); + } +}