Introduce ctl::set and ctl::map

We now have a C++ red-black tree implementation that implements standard
template library compatible APIs while compiling 10x faster than libcxx.
It's not as beautiful as the red-black tree implementation in Plinko but
this will get the job done and the test proves it upholds all invariants

This change also restores CheckForMemoryLeaks() support and fixes a real
actual bug I discovered with Doug Lea's dlmalloc_inspect_all() function.
This commit is contained in:
Justine Tunney 2024-06-23 10:08:48 -07:00
parent 388e236360
commit c4c812c154
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
45 changed files with 2358 additions and 135 deletions

64
ctl/equal.h Normal file
View file

@ -0,0 +1,64 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_EQUAL_H_
#define CTL_EQUAL_H_
namespace ctl {
template<class InputIt1, class InputIt2>
bool
equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)
{
for (; first1 != last1; ++first1, ++first2) {
if (!(*first1 == *first2)) {
return false;
}
}
return true;
}
// Overload that takes a predicate
template<class InputIt1, class InputIt2, class BinaryPredicate>
bool
equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, BinaryPredicate pred)
{
for (; first1 != last1; ++first1, ++first2) {
if (!pred(*first1, *first2)) {
return false;
}
}
return true;
}
// Overloads that take two ranges (C++14 and later)
template<class InputIt1, class InputIt2>
bool
equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
{
for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
if (!(*first1 == *first2)) {
return false;
}
}
return first1 == last1 && first2 == last2;
}
template<class InputIt1, class InputIt2, class BinaryPredicate>
bool
equal(InputIt1 first1,
InputIt1 last1,
InputIt2 first2,
InputIt2 last2,
BinaryPredicate pred)
{
for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
if (!pred(*first1, *first2)) {
return false;
}
}
return first1 == last1 && first2 == last2;
}
} // namespace ctl
#endif /* CTL_EQUAL_H_ */

64
ctl/initializer_list.h Normal file
View file

@ -0,0 +1,64 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef _LIBCPP_INITIALIZER_LIST
#define _LIBCPP_INITIALIZER_LIST
namespace std {
template<class T>
class initializer_list
{
constexpr initializer_list(const T* b, size_t s) noexcept
: begin_(b), size_(s)
{
}
public:
typedef T value_type;
typedef const T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef const T* iterator;
typedef const T* const_iterator;
constexpr initializer_list() noexcept : begin_(nullptr), size_(0)
{
}
constexpr size_t size() const noexcept
{
return size_;
}
constexpr const T* begin() const noexcept
{
return begin_;
}
constexpr const T* end() const noexcept
{
return begin_ + size_;
}
private:
const T* begin_;
size_t size_;
};
template<class T>
inline constexpr const T*
begin(initializer_list<T> i) noexcept
{
return i.begin();
}
template<class T>
inline constexpr const T*
end(initializer_list<T> i) noexcept
{
return i.end();
}
} // namespace std
#endif // _LIBCPP_INITIALIZER_LIST

38
ctl/less.h Normal file
View file

@ -0,0 +1,38 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_LESS_H_
#define CTL_LESS_H_
#include "utility.h"
namespace ctl {
template<class T = void>
struct less
{
constexpr bool operator()(const T& lhs, const T& rhs) const
{
return lhs < rhs;
}
typedef T first_argument_type;
typedef T second_argument_type;
typedef bool result_type;
};
template<>
struct less<void>
{
template<class T, class U>
constexpr auto operator()(T&& lhs,
U&& rhs) const -> decltype(ctl::forward<T>(lhs) <
ctl::forward<U>(rhs))
{
return ctl::forward<T>(lhs) < ctl::forward<U>(rhs);
}
typedef void is_transparent;
};
} // namespace ctl
#endif /* CTL_LESS_H_ */

352
ctl/map.h Normal file
View file

@ -0,0 +1,352 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_MAP_H_
#define CTL_MAP_H_
#include "set.h"
namespace ctl {
template<typename Key, typename Value, typename Compare = ctl::less<Key>>
class map
{
class EntryCompare
{
public:
explicit EntryCompare(Compare comp = Compare()) : comp_(comp)
{
}
bool operator()(const ctl::pair<const Key, Value>& lhs,
const ctl::pair<const Key, Value>& rhs) const
{
return comp_(lhs.first, rhs.first);
}
private:
Compare comp_;
};
ctl::set<ctl::pair<const Key, Value>, EntryCompare> data_;
public:
using key_type = Key;
using mapped_type = Value;
using value_type = ctl::pair<const Key, Value>;
using size_type = typename ctl::set<value_type, EntryCompare>::size_type;
using difference_type =
typename ctl::set<value_type, EntryCompare>::difference_type;
using key_compare = Compare;
using value_compare = EntryCompare;
using iterator = typename ctl::set<value_type, EntryCompare>::iterator;
using const_iterator =
typename ctl::set<value_type, EntryCompare>::const_iterator;
using reverse_iterator =
typename ctl::set<value_type, EntryCompare>::reverse_iterator;
using const_reverse_iterator =
typename ctl::set<value_type, EntryCompare>::const_reverse_iterator;
map() : data_(EntryCompare())
{
}
explicit map(const Compare& comp) : data_(EntryCompare(comp))
{
}
map(const map& other) = default;
map(map&& other) noexcept = default;
map(std::initializer_list<value_type> init, const Compare& comp = Compare())
: data_(init, EntryCompare(comp))
{
}
template<typename InputIt>
map(InputIt first, InputIt last, const Compare& comp = Compare())
: data_(first, last, EntryCompare(comp))
{
}
map& operator=(const map& other) = default;
map& operator=(map&& other) noexcept = default;
map& operator=(std::initializer_list<value_type> ilist)
{
data_ = ilist;
return *this;
}
iterator begin() noexcept
{
return data_.begin();
}
const_iterator begin() const noexcept
{
return data_.begin();
}
const_iterator cbegin() const noexcept
{
return data_.cbegin();
}
iterator end() noexcept
{
return data_.end();
}
const_iterator end() const noexcept
{
return data_.end();
}
const_iterator cend() const noexcept
{
return data_.cend();
}
reverse_iterator rbegin() noexcept
{
return data_.rbegin();
}
const_reverse_iterator rbegin() const noexcept
{
return data_.rbegin();
}
const_reverse_iterator crbegin() const noexcept
{
return data_.crbegin();
}
reverse_iterator rend() noexcept
{
return data_.rend();
}
const_reverse_iterator rend() const noexcept
{
return data_.rend();
}
const_reverse_iterator crend() const noexcept
{
return data_.crend();
}
bool empty() const noexcept
{
return data_.empty();
}
size_type size() const noexcept
{
return data_.size();
}
size_type max_size() const noexcept
{
return data_.max_size();
}
Value& operator[](const Key& key)
{
return ((data_.insert(make_pair(key, Value()))).first)->second;
}
Value& operator[](Key&& key)
{
return ((data_.insert(make_pair(ctl::move(key), Value()))).first)
->second;
}
Value& at(const Key& key)
{
auto it = find(key);
if (it == end())
__builtin_trap();
return it->second;
}
const Value& at(const Key& key) const
{
auto it = find(key);
if (it == end())
__builtin_trap();
return it->second;
}
ctl::pair<iterator, bool> insert(const value_type& value)
{
return data_.insert(value);
}
ctl::pair<iterator, bool> insert(value_type&& value)
{
return data_.insert(ctl::move(value));
}
template<typename P>
ctl::pair<iterator, bool> insert(P&& value)
{
return data_.insert(value_type(ctl::forward<P>(value)));
}
iterator insert(const_iterator hint, const value_type& value)
{
return data_.insert(hint, value);
}
iterator insert(const_iterator hint, value_type&& value)
{
return data_.insert(hint, ctl::move(value));
}
template<typename P>
iterator insert(const_iterator hint, P&& value)
{
return data_.insert(hint, value_type(ctl::forward<P>(value)));
}
template<typename InputIt>
void insert(InputIt first, InputIt last)
{
data_.insert(first, last);
}
void insert(std::initializer_list<value_type> ilist)
{
data_.insert(ilist);
}
template<typename... Args>
ctl::pair<iterator, bool> emplace(Args&&... args)
{
return data_.emplace(ctl::forward<Args>(args)...);
}
template<typename... Args>
iterator emplace_hint(const_iterator hint, Args&&... args)
{
return data_.emplace_hint(hint, ctl::forward<Args>(args)...);
}
iterator erase(const_iterator pos)
{
return data_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
return data_.erase(first, last);
}
size_type erase(const Key& key)
{
return data_.erase(make_pair(key, Value()));
}
void swap(map& other) noexcept
{
data_.swap(other.data_);
}
void clear() noexcept
{
data_.clear();
}
iterator find(const Key& key)
{
return data_.find(make_pair(key, Value()));
}
const_iterator find(const Key& key) const
{
return data_.find(make_pair(key, Value()));
}
size_type count(const Key& key) const
{
return data_.count(make_pair(key, Value()));
}
iterator lower_bound(const Key& key)
{
return data_.lower_bound(make_pair(key, Value()));
}
const_iterator lower_bound(const Key& key) const
{
return data_.lower_bound(make_pair(key, Value()));
}
iterator upper_bound(const Key& key)
{
return data_.upper_bound(make_pair(key, Value()));
}
const_iterator upper_bound(const Key& key) const
{
return data_.upper_bound(make_pair(key, Value()));
}
ctl::pair<iterator, iterator> equal_range(const Key& key)
{
return data_.equal_range(make_pair(key, Value()));
}
ctl::pair<const_iterator, const_iterator> equal_range(const Key& key) const
{
return data_.equal_range(make_pair(key, Value()));
}
key_compare key_comp() const
{
return data_.value_comp().comp;
}
value_compare value_comp() const
{
return data_.value_comp();
}
friend bool operator==(const map& lhs, const map& rhs)
{
return lhs.data_ == rhs.data_;
}
friend bool operator!=(const map& lhs, const map& rhs)
{
return !(lhs == rhs);
}
friend bool operator<(const map& lhs, const map& rhs)
{
return lhs.data_ < rhs.data_;
}
friend bool operator<=(const map& lhs, const map& rhs)
{
return !(rhs < lhs);
}
friend bool operator>(const map& lhs, const map& rhs)
{
return rhs < lhs;
}
friend bool operator>=(const map& lhs, const map& rhs)
{
return !(lhs < rhs);
}
friend void swap(map& lhs, map& rhs) noexcept
{
lhs.swap(rhs);
}
};
} // namespace ctl
#endif // CTL_MAP_H_

View file

@ -34,7 +34,10 @@ _ctl_alloc(size_t n, size_t a)
static void*
_ctl_alloc1(size_t n)
{
return _ctl_alloc(n, 1);
void* p;
if (!(p = malloc(n)))
__builtin_trap();
return p;
}
static void*
@ -68,10 +71,21 @@ COSMOPOLITAN_C_END_
now. If you have any brain cells left after reading this comment then go
look at the eight operator delete weak references to free in the below. */
__weak_reference(void* operator new(size_t, ctl::align_val_t), _ctl_alloc);
__weak_reference(void* operator new[](size_t, ctl::align_val_t), _ctl_alloc);
__weak_reference(void* operator new(size_t), _ctl_alloc1);
__weak_reference(void* operator new[](size_t), _ctl_alloc1);
__weak_reference(void*
operator new(size_t, ctl::align_val_t),
_ctl_alloc);
__weak_reference(void*
operator new[](size_t, ctl::align_val_t),
_ctl_alloc);
__weak_reference(void*
operator new(size_t),
_ctl_alloc1);
__weak_reference(void*
operator new[](size_t),
_ctl_alloc1);
// XXX clang-format currently mutilates these for some reason.
// clang-format off

View file

@ -3,6 +3,10 @@
#ifndef COSMOPOLITAN_CTL_NEW_H_
#define COSMOPOLITAN_CTL_NEW_H_
#ifndef TINY
__static_yoink("__demangle");
#endif
namespace ctl {
enum class align_val_t : size_t

150
ctl/pair.h Normal file
View file

@ -0,0 +1,150 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_PAIR_H_
#define CTL_PAIR_H_
#include "utility.h"
namespace ctl {
template<typename T1, typename T2>
struct pair
{
using first_type = T1;
using second_type = T2;
T1 first;
T2 second;
constexpr pair() : first(), second()
{
}
constexpr pair(const T1& x, const T2& y) : first(x), second(y)
{
}
constexpr pair(const pair<T1, T2>& p) : first(p.first), second(p.second)
{
}
template<class U1, class U2>
constexpr pair(U1&& x, U2&& y)
: first(ctl::forward<U1>(x)), second(ctl::forward<U2>(y))
{
}
template<class U1, class U2>
constexpr pair(const pair<U1, U2>& p) : first(p.first), second(p.second)
{
}
constexpr pair(pair<T1, T2>&& p)
: first(ctl::move(p.first)), second(ctl::move(p.second))
{
}
template<class U1, class U2>
constexpr pair(pair<U1, U2>&& p)
: first(ctl::move(p.first)), second(ctl::move(p.second))
{
}
pair& operator=(const pair& other)
{
first = other.first;
second = other.second;
return *this;
}
pair& operator=(pair&& other) noexcept
{
first = ctl::move(other.first);
second = ctl::move(other.second);
return *this;
}
template<class U1, class U2>
pair& operator=(const pair<U1, U2>& other)
{
first = other.first;
second = other.second;
return *this;
}
template<class U1, class U2>
pair& operator=(pair<U1, U2>&& other)
{
first = ctl::move(other.first);
second = ctl::move(other.second);
return *this;
}
void swap(pair& other) noexcept
{
using ctl::swap;
swap(first, other.first);
swap(second, other.second);
}
};
template<class T1, class T2>
constexpr pair<T1, T2>
make_pair(T1&& t, T2&& u)
{
return pair<T1, T2>(ctl::forward<T1>(t), ctl::forward<T2>(u));
}
// Comparison operators
template<class T1, class T2>
constexpr bool
operator==(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
return lhs.first == rhs.first && lhs.second == rhs.second;
}
template<class T1, class T2>
constexpr bool
operator!=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
return !(lhs == rhs);
}
template<class T1, class T2>
constexpr bool
operator<(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
return lhs.first < rhs.first ||
(!(rhs.first < lhs.first) && lhs.second < rhs.second);
}
template<class T1, class T2>
constexpr bool
operator<=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
return !(rhs < lhs);
}
template<class T1, class T2>
constexpr bool
operator>(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
return rhs < lhs;
}
template<class T1, class T2>
constexpr bool
operator>=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
return !(lhs < rhs);
}
template<class T1, class T2>
void
swap(pair<T1, T2>& lhs, pair<T1, T2>& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
} // namespace ctl
#endif // CTL_PAIR_H_

945
ctl/set.h Normal file
View file

@ -0,0 +1,945 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef CTL_SET_H_
#define CTL_SET_H_
#include "initializer_list.h"
#include "less.h"
#include "pair.h"
namespace ctl {
template<typename Key, typename Compare = less<Key>>
class set
{
struct rbtree
{
rbtree* left;
rbtree* right;
rbtree* parent;
bool is_red;
Key value;
rbtree(const Key& val)
: left(nullptr)
, right(nullptr)
, parent(nullptr)
, is_red(true)
, value(val)
{
}
};
public:
using key_type = Key;
using value_type = Key;
using size_type = size_t;
using node_type = rbtree;
using key_compare = Compare;
using value_compare = Compare;
using difference_type = ptrdiff_t;
using reference = value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using const_reference = const value_type&;
class iterator
{
public:
using value_type = Key;
using difference_type = ptrdiff_t;
using pointer = Key*;
using reference = Key&;
iterator() : node_(nullptr)
{
}
reference operator*() const
{
return node_->value;
}
pointer operator->() const
{
return &(node_->value);
}
iterator& operator++()
{
if (node_ == nullptr)
return *this;
if (node_->right) {
node_ = leftmost(node_->right);
} else {
node_type* parent = node_->parent;
while (parent && node_ == parent->right) {
node_ = parent;
parent = parent->parent;
}
node_ = parent;
}
return *this;
}
iterator& operator--()
{
if (node_ == nullptr)
__builtin_trap();
if (node_->left) {
node_ = rightmost(node_->left);
} else {
node_type* parent = node_->parent;
for (;;) {
if (parent == nullptr)
break;
if (node_ == parent->left) {
node_ = parent;
parent = parent->parent;
} else {
break;
}
}
node_ = parent;
}
return *this;
}
iterator operator++(int)
{
iterator tmp = *this;
++(*this);
return tmp;
}
iterator operator--(int)
{
iterator tmp = *this;
--(*this);
return tmp;
}
bool operator==(const iterator& other) const
{
return node_ == other.node_;
}
bool operator!=(const iterator& other) const
{
return !(*this == other);
}
private:
friend class set;
node_type* node_;
explicit iterator(node_type* node) : node_(node)
{
}
};
class reverse_iterator
{
public:
using value_type = Key;
using difference_type = ptrdiff_t;
using pointer = Key*;
using reference = Key&;
reverse_iterator() : node_(nullptr)
{
}
reference operator*() const
{
return node_->value;
}
pointer operator->() const
{
return &(node_->value);
}
reverse_iterator& operator++()
{
if (node_->left) {
node_ = rightmost(node_->left);
} else {
node_type* parent = node_->parent;
while (parent && node_ == parent->left) {
node_ = parent;
parent = parent->parent;
}
node_ = parent;
}
return *this;
}
reverse_iterator operator++(int)
{
reverse_iterator tmp = *this;
++(*this);
return tmp;
}
reverse_iterator& operator--()
{
if (node_->right) {
node_ = leftmost(node_->right);
} else {
node_type* parent = node_->parent;
while (parent && node_ == parent->right) {
node_ = parent;
parent = parent->parent;
}
node_ = parent;
}
return *this;
}
reverse_iterator operator--(int)
{
reverse_iterator tmp = *this;
--(*this);
return tmp;
}
bool operator==(const reverse_iterator& other) const
{
return node_ == other.node_;
}
bool operator!=(const reverse_iterator& other) const
{
return !(*this == other);
}
iterator base() const
{
if (node_ == nullptr)
return iterator(leftmost(root_));
reverse_iterator tmp = *this;
++tmp;
return iterator(tmp.node_);
}
private:
friend class set;
node_type* node_;
explicit reverse_iterator(node_type* node) : node_(node)
{
}
};
using const_iterator = iterator;
using const_reverse_iterator = reverse_iterator;
set() : root_(nullptr), size_(0), comp_(Compare())
{
}
explicit set(const Compare& comp) : root_(nullptr), size_(0), comp_(comp)
{
}
template<class InputIt>
set(InputIt first, InputIt last, const Compare& comp = Compare())
: root_(nullptr), size_(0), comp_(comp)
{
for (; first != last; ++first)
insert(*first);
}
set(const set& other) : root_(nullptr), size_(0)
{
if (other.root_) {
root_ = copier(other.root_);
size_ = other.size_;
}
}
set(set&& other) noexcept : root_(other.root_), size_(other.size_)
{
other.root_ = nullptr;
other.size_ = 0;
}
set(std::initializer_list<value_type> init, const Compare& comp = Compare())
: root_(nullptr), size_(0), comp_(comp)
{
for (const auto& value : init)
insert(value);
}
~set()
{
clear();
}
set& operator=(const set& other)
{
if (this != &other) {
clear();
if (other.root_) {
root_ = copier(other.root_);
size_ = other.size_;
}
}
return *this;
}
set& operator=(set&& other) noexcept
{
if (this != &other) {
clear();
root_ = other.root_;
size_ = other.size_;
other.root_ = nullptr;
other.size_ = 0;
}
return *this;
}
bool empty() const noexcept
{
return size_ == 0;
}
size_type size() const noexcept
{
return size_;
}
iterator begin() noexcept
{
if (root_)
return iterator(leftmost(root_));
return iterator(nullptr);
}
const_iterator begin() const noexcept
{
if (root_)
return const_iterator(leftmost(root_));
return const_iterator(nullptr);
}
const_iterator cbegin() const noexcept
{
return begin();
}
reverse_iterator rbegin()
{
return reverse_iterator(rightmost(root_));
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(rightmost(root_));
}
const_reverse_iterator crbegin() const
{
return const_reverse_iterator(rightmost(root_));
}
iterator end() noexcept
{
return iterator(nullptr);
}
const_iterator end() const noexcept
{
return const_iterator(nullptr);
}
const_iterator cend() const noexcept
{
return end();
}
reverse_iterator rend()
{
return reverse_iterator(nullptr);
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(nullptr);
}
const_reverse_iterator crend() const
{
return const_reverse_iterator(nullptr);
}
void clear() noexcept
{
clearer(root_);
root_ = nullptr;
size_ = 0;
}
pair<iterator, bool> insert(value_type&& value)
{
return insert_node(new node_type(ctl::move(value)));
}
pair<iterator, bool> insert(const value_type& value)
{
return insert_node(new node_type(value));
}
iterator insert(const_iterator hint, const value_type& value)
{
return insert(value).first;
}
iterator insert(const_iterator hint, value_type&& value)
{
return insert(ctl::move(value)).first;
}
template<class InputIt>
void insert(InputIt first, InputIt last)
{
for (; first != last; ++first)
insert(*first);
}
void insert(std::initializer_list<value_type> ilist)
{
for (const auto& value : ilist)
insert(value);
}
template<class... Args>
pair<iterator, bool> emplace(Args&&... args)
{
value_type value(ctl::forward<Args>(args)...);
return insert(ctl::move(value));
}
iterator erase(iterator pos)
{
node_type* node = pos.node_;
iterator res = ++pos;
erase_node(node);
return res;
}
size_type erase(const key_type& key)
{
node_type* node = get_element(key);
if (node == nullptr)
return 0;
erase_node(node);
return 1;
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last)
first = erase(first);
return iterator(const_cast<node_type*>(last.node_));
}
void swap(set& other) noexcept
{
ctl::swap(root_, other.root_);
ctl::swap(size_, other.size_);
}
pair<iterator, iterator> equal_range(const key_type& key)
{
return { iterator(get_floor(key)), iterator(get_ceiling(key)) };
}
pair<const_iterator, const_iterator> equal_range(const key_type& key) const
{
return { const_iterator(get_floor(key)),
const_iterator(get_ceiling(key)) };
}
iterator lower_bound(const key_type& key)
{
return iterator(get_floor(key));
}
const_iterator lower_bound(const key_type& key) const
{
return const_iterator(get_floor(key));
}
iterator upper_bound(const key_type& key)
{
return iterator(get_ceiling(key));
}
const_iterator upper_bound(const key_type& key) const
{
return const_iterator(get_ceiling(key));
}
size_type count(const key_type& key) const
{
return !!get_element(key);
}
iterator find(const key_type& key)
{
return iterator(get_element(key));
}
const_iterator find(const key_type& key) const
{
return const_iterator(get_element(key));
}
template<class... Args>
iterator emplace_hint(const_iterator hint, Args&&... args)
{
return emplace(ctl::forward<Args>(args)...).first;
}
void check() const
{
size_type count = 0;
if (root_ != nullptr) {
if (root_->is_red)
// ILLEGAL TREE: root node must be black
__builtin_trap();
int black_height = -1;
checker(root_, nullptr, 0, black_height);
count = tally(root_);
}
if (count != size_)
// ILLEGAL TREE: unexpected number of nodes
__builtin_trap();
}
private:
static node_type* leftmost(node_type* node) noexcept
{
while (node && node->left)
node = node->left;
return node;
}
static node_type* rightmost(node_type* node) noexcept
{
while (node && node->right)
node = node->right;
return node;
}
static void clearer(node_type* node) noexcept
{
node_type* right;
for (; node; node = right) {
right = node->right;
clearer(node->left);
delete node;
}
}
static node_type* copier(const node_type* node)
{
if (node == nullptr)
return nullptr;
node_type* new_node = new node_type(node->value);
new_node->left = copier(node->left);
new_node->right = copier(node->right);
if (new_node->left)
new_node->left->parent = new_node;
if (new_node->right)
new_node->right->parent = new_node;
return new_node;
}
static size_type tally(const node_type* node)
{
if (node == nullptr)
return 0;
return 1 + tally(node->left) + tally(node->right);
}
template<typename K>
node_type* get_element(const K& key) const
{
node_type* current = root_;
while (current != nullptr) {
if (comp_(key, current->value)) {
current = current->left;
} else if (comp_(current->value, key)) {
current = current->right;
} else {
return current;
}
}
return nullptr;
}
template<typename K>
node_type* get_floor(const K& key) const
{
node_type* result = nullptr;
node_type* current = root_;
while (current != nullptr) {
if (!comp_(current->value, key)) {
result = current;
current = current->left;
} else {
current = current->right;
}
}
return result;
}
template<typename K>
node_type* get_ceiling(const K& key) const
{
node_type* result = nullptr;
node_type* current = root_;
while (current != nullptr) {
if (comp_(key, current->value)) {
result = current;
current = current->left;
} else {
current = current->right;
}
}
return result;
}
pair<iterator, bool> insert_node(node_type* node)
{
if (root_ == nullptr) {
root_ = node;
root_->is_red = false;
size_++;
return { iterator(root_), true };
}
node_type* current = root_;
node_type* parent = nullptr;
while (current != nullptr) {
parent = current;
if (comp_(node->value, current->value)) {
current = current->left;
} else if (comp_(current->value, node->value)) {
current = current->right;
} else {
delete node; // already exists
return { iterator(current), false };
}
}
if (comp_(node->value, parent->value)) {
parent->left = node;
} else {
parent->right = node;
}
node->parent = parent;
size_++;
rebalance_after_insert(node);
return { iterator(node), true };
}
void erase_node(node_type* node)
{
node_type* y = node;
node_type* x = nullptr;
node_type* x_parent = nullptr;
bool y_original_color = y->is_red;
if (node->left == nullptr) {
x = node->right;
transplant(node, node->right);
x_parent = node->parent;
} else if (node->right == nullptr) {
x = node->left;
transplant(node, node->left);
x_parent = node->parent;
} else {
y = leftmost(node->right);
y_original_color = y->is_red;
x = y->right;
if (y->parent == node) {
if (x)
x->parent = y;
x_parent = y;
} else {
transplant(y, y->right);
y->right = node->right;
y->right->parent = y;
x_parent = y->parent;
}
transplant(node, y);
y->left = node->left;
y->left->parent = y;
y->is_red = node->is_red;
}
if (!y_original_color)
rebalance_after_erase(x, x_parent);
delete node;
--size_;
}
void left_rotate(node_type* x)
{
node_type* y = x->right;
x->right = y->left;
if (y->left != nullptr)
y->left->parent = x;
y->parent = x->parent;
if (x->parent == nullptr) {
root_ = y;
} else if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
y->left = x;
x->parent = y;
}
void right_rotate(node_type* y)
{
node_type* x = y->left;
y->left = x->right;
if (x->right != nullptr)
x->right->parent = y;
x->parent = y->parent;
if (y->parent == nullptr) {
root_ = x;
} else if (y == y->parent->right) {
y->parent->right = x;
} else {
y->parent->left = x;
}
x->right = y;
y->parent = x;
}
void transplant(node_type* u, node_type* v)
{
if (u->parent == nullptr) {
root_ = v;
} else if (u == u->parent->left) {
u->parent->left = v;
} else {
u->parent->right = v;
}
if (v != nullptr)
v->parent = u->parent;
}
void checker(const node_type* node,
const node_type* parent,
int black_count,
int& black_height) const
{
if (node == nullptr) {
// Leaf nodes are considered black
if (black_height == -1) {
black_height = black_count;
} else if (black_count != black_height) {
// ILLEGAL TREE: Black height mismatch
__builtin_trap();
}
return;
}
if (node->parent != parent)
// ILLEGAL TREE: Parent link is incorrect
__builtin_trap();
if (parent) {
if (parent->left == node && !comp_(node->value, parent->value))
// ILLEGAL TREE: Binary search property violated on left child
__builtin_trap();
if (parent->right == node && !comp_(parent->value, node->value))
// ILLEGAL TREE: Binary search property violated on right child
__builtin_trap();
}
if (!node->is_red) {
black_count++;
} else if (parent != nullptr && parent->is_red) {
// ILLEGAL TREE: Red node has red child
__builtin_trap();
}
checker(node->left, node, black_count, black_height);
checker(node->right, node, black_count, black_height);
}
void rebalance_after_insert(node_type* node)
{
node->is_red = true;
while (node != root_ && node->parent->is_red) {
if (node->parent == node->parent->parent->left) {
node_type* uncle = node->parent->parent->right;
if (uncle && uncle->is_red) {
node->parent->is_red = false;
uncle->is_red = false;
node->parent->parent->is_red = true;
node = node->parent->parent;
} else {
if (node == node->parent->right) {
node = node->parent;
left_rotate(node);
}
node->parent->is_red = false;
node->parent->parent->is_red = true;
right_rotate(node->parent->parent);
}
} else {
node_type* uncle = node->parent->parent->left;
if (uncle && uncle->is_red) {
node->parent->is_red = false;
uncle->is_red = false;
node->parent->parent->is_red = true;
node = node->parent->parent;
} else {
if (node == node->parent->left) {
node = node->parent;
right_rotate(node);
}
node->parent->is_red = false;
node->parent->parent->is_red = true;
left_rotate(node->parent->parent);
}
}
}
root_->is_red = false;
}
void rebalance_after_erase(node_type* node, node_type* parent)
{
while (node != root_ && (node == nullptr || !node->is_red)) {
if (node == parent->left) {
node_type* sibling = parent->right;
if (sibling->is_red) {
sibling->is_red = false;
parent->is_red = true;
left_rotate(parent);
sibling = parent->right;
}
if ((sibling->left == nullptr || !sibling->left->is_red) &&
(sibling->right == nullptr || !sibling->right->is_red)) {
sibling->is_red = true;
node = parent;
parent = node->parent;
} else {
if (sibling->right == nullptr || !sibling->right->is_red) {
sibling->left->is_red = false;
sibling->is_red = true;
right_rotate(sibling);
sibling = parent->right;
}
sibling->is_red = parent->is_red;
parent->is_red = false;
sibling->right->is_red = false;
left_rotate(parent);
node = root_;
break;
}
} else {
node_type* sibling = parent->left;
if (sibling->is_red) {
sibling->is_red = false;
parent->is_red = true;
right_rotate(parent);
sibling = parent->left;
}
if ((sibling->right == nullptr || !sibling->right->is_red) &&
(sibling->left == nullptr || !sibling->left->is_red)) {
sibling->is_red = true;
node = parent;
parent = node->parent;
} else {
if (sibling->left == nullptr || !sibling->left->is_red) {
sibling->right->is_red = false;
sibling->is_red = true;
left_rotate(sibling);
sibling = parent->left;
}
sibling->is_red = parent->is_red;
parent->is_red = false;
sibling->left->is_red = false;
right_rotate(parent);
node = root_;
break;
}
}
}
if (node != nullptr)
node->is_red = false;
}
node_type* root_;
size_type size_;
Compare comp_;
};
template<class Key, typename Compare = less<Key>>
bool
operator==(const set<Key, Compare>& lhs, const set<Key, Compare>& rhs)
{
if (lhs.size() != rhs.size())
return false;
auto i = lhs.cbegin();
auto j = rhs.cbegin();
for (; i != lhs.end(); ++i, ++j) {
if (!(*i == *j))
return false;
}
return true;
}
template<class Key, typename Compare = less<Key>>
bool
operator<(const set<Key, Compare>& lhs, const set<Key, Compare>& rhs)
{
auto i = lhs.cbegin();
auto j = rhs.cbegin();
for (; i != lhs.end() && j != rhs.end(); ++i, ++j) {
if (Compare()(*i, *j))
return true;
if (Compare()(*j, *i))
return false;
}
return i == lhs.end() && j != rhs.end();
}
template<class Key, typename Compare = less<Key>>
bool
operator!=(const set<Key, Compare>& lhs, const set<Key, Compare>& rhs)
{
return !(lhs == rhs);
}
template<class Key, typename Compare = less<Key>>
bool
operator<=(const set<Key, Compare>& lhs, const set<Key, Compare>& rhs)
{
return !(rhs < lhs);
}
template<class Key, typename Compare = less<Key>>
bool
operator>(const set<Key, Compare>& lhs, const set<Key, Compare>& rhs)
{
return rhs < lhs;
}
template<class Key, typename Compare = less<Key>>
bool
operator>=(const set<Key, Compare>& lhs, const set<Key, Compare>& rhs)
{
return !(lhs < rhs);
}
template<class Key, typename Compare = less<Key>>
void
swap(set<Key, Compare>& lhs, set<Key, Compare>& rhs) noexcept;
} // namespace ctl
#endif // CTL_SET_H_

View file

@ -1,7 +1,7 @@
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
#ifndef COSMOPOLITAN_CTL_STRING_H_
#define COSMOPOLITAN_CTL_STRING_H_
#ifndef CTL_STRING_H_
#define CTL_STRING_H_
#include "string_view.h"
namespace ctl {
@ -397,4 +397,4 @@ operator"" s(const char* s, size_t n)
}
#pragma GCC diagnostic pop
#endif // COSMOPOLITAN_CTL_STRING_H_
#endif // CTL_STRING_H_

View file

@ -23,6 +23,7 @@
#include <sys/auxv.h>
#include <sys/socket.h>
#include <time.h>
#include "libc/mem/leaks.h"
/**
* @fileoverview greenbean lightweight threaded web server
@ -411,7 +412,5 @@ int main(int argc, char *argv[]) {
unassert(!pthread_mutex_destroy(&statuslock));
// quality assurance
if (IsModeDbg()) {
CheckForMemoryLeaks();
}
CheckForMemoryLeaks();
}

View file

@ -28,11 +28,11 @@
#include "libc/calls/wincrash.internal.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/leaky.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/log/backtrace.internal.h"
#include "libc/macros.internal.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/nt/createfile.h"
#include "libc/nt/enum/fileflagandattributes.h"
@ -75,7 +75,7 @@ static textwindows struct FileLock *NewFileLock(void) {
fl = g_locks.free;
g_locks.free = fl->next;
} else {
unassert((fl = _weaken(malloc)(sizeof(*fl))));
unassert((fl = may_leak(_weaken(malloc)(sizeof(*fl)))));
}
bzero(fl, sizeof(*fl));
fl->next = g_locks.list;
@ -83,8 +83,6 @@ static textwindows struct FileLock *NewFileLock(void) {
return fl;
}
IGNORE_LEAKS(NewFileLock)
static textwindows void FreeFileLock(struct FileLock *fl) {
fl->next = g_locks.free;
g_locks.free = fl;

View file

@ -1,12 +0,0 @@
#ifndef COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_
#define COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_
#include "libc/dce.h"
COSMOPOLITAN_C_START_
#define IGNORE_LEAKS(FUNC)
extern intptr_t _leaky_end[] __attribute__((__weak__));
extern intptr_t _leaky_start[] __attribute__((__weak__));
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_INTRIN_LEAKY_INTERNAL_H_ */

View file

@ -43,6 +43,7 @@
#include "libc/intrin/weaken.h"
#include "libc/mem/critbit0.h"
#include "libc/mem/gc.h"
#include "libc/mem/leaks.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/nexgen32e/x86feature.h"

View file

@ -20,7 +20,6 @@
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigaltstack.h"
#include "libc/calls/struct/sigset.h"
#include "libc/intrin/leaky.internal.h"
#include "libc/log/internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
@ -93,5 +92,3 @@ void ShowCrashReports(void) {
InstallCrashHandler(SIGABRT, 0);
InstallCrashHandler(SIGSEGV, SA_ONSTACK);
}
IGNORE_LEAKS(ShowCrashReports)

View file

@ -16,10 +16,79 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/cxxabi.h"
#include "libc/intrin/cxaatexit.internal.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/weaken.h"
#include "libc/mem/mem.h"
#include "libc/nt/typedef/imagetlscallback.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#define LEAK_CONTAINER(e) DLL_CONTAINER(struct Leak, elem, e)
struct Leak {
void *alloc;
struct Dll elem;
};
static int leak_count;
static struct Dll *leaks;
static struct Dll *freaks;
static pthread_mutex_t lock;
void __may_leak(void *alloc) {
if (!alloc)
return;
pthread_mutex_lock(&lock);
if (dll_is_empty(freaks)) {
int g = __granularity();
struct Leak *p = _mapanon(g);
int n = g / sizeof(struct Leak);
for (int i = 0; i < n; ++i) {
dll_init(&p[i].elem);
dll_make_first(&freaks, &p[i].elem);
}
}
struct Dll *e = dll_first(freaks);
LEAK_CONTAINER(e)->alloc = alloc;
dll_remove(&freaks, e);
dll_make_first(&leaks, e);
pthread_mutex_unlock(&lock);
}
static void visitor(void *start, void *end, size_t used_bytes, void *arg) {
if (!used_bytes)
return;
for (struct Dll *e = dll_first(leaks); e; e = dll_next(leaks, e))
if (start == LEAK_CONTAINER(e)->alloc)
return;
kprintf("error: leaked %'zu byte allocation at %p\n", used_bytes, start);
++leak_count;
}
/**
* Tests for memory leaks.
*/
void CheckForMemoryLeaks(void) {
// TODO(jart): give me a new lease on life
// validate usage of this api
if (_weaken(_pthread_decimate))
_weaken(_pthread_decimate)();
if (!pthread_orphan_np())
kprintf("warning: called CheckForMemoryLeaks() from non-orphaned thread\n");
// call dynamic global destructors
__cxa_thread_finalize();
__cxa_finalize(0);
// check for leaks
malloc_inspect_all(visitor, 0);
if (leak_count) {
kprintf("loser: you forgot to call free %'d time%s\n", leak_count,
leak_count == 1 ? "" : "s");
_exit(73);
}
}

22
libc/mem/leaks.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef COSMOPOLITAN_LIBC_MEM_LEAKS_H_
#define COSMOPOLITAN_LIBC_MEM_LEAKS_H_
#include "libc/intrin/weaken.h"
COSMOPOLITAN_C_START_
void CheckForMemoryLeaks(void) libcesque;
/**
* Declares that allocation needn't be freed.
*
* This function does nothing if CheckForMemoryLeaks() hasn't been
* linked into the binary.
*/
forceinline void *may_leak(void *__p) {
void __may_leak(void *) libcesque;
if (_weaken(__may_leak))
_weaken(__may_leak)(__p);
return __p;
}
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_MEM_LEAKS_H_ */

View file

@ -17,10 +17,10 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/getenv.internal.h"
#include "libc/intrin/leaky.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/mem/internal.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/errfuns.h"
@ -42,7 +42,7 @@ static char **__growenv(char **a) {
a = environ;
n = a ? __lenenv(a) : 0;
c = MAX(8ul, n) << 1;
if ((b = malloc(c * sizeof(char *)))) {
if ((b = may_leak(malloc(c * sizeof(char *))))) {
if (a) {
for (p = b; *a;) {
*p++ = *a++;
@ -59,8 +59,6 @@ static char **__growenv(char **a) {
}
}
IGNORE_LEAKS(__growenv)
int __putenv(char *s, bool overwrite) {
char **p;
struct Env e;

View file

@ -16,9 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/leaky.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/mem/internal.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
@ -39,7 +39,8 @@ int setenv(const char *name, const char *value, int overwrite) {
size_t n, m;
if (!name || !*name || !value || strchr(name, '='))
return einval();
if ((s = malloc((n = strlen(name)) + 1 + (m = strlen(value)) + 1))) {
if ((s = may_leak(
malloc((n = strlen(name)) + 1 + (m = strlen(value)) + 1)))) {
memcpy(mempcpy(mempcpy(s, name, n), "=", 1), value, m + 1);
rc = __putenv(s, overwrite);
} else {
@ -48,5 +49,3 @@ int setenv(const char *name, const char *value, int overwrite) {
STRACE("setenv(%#s, %#s, %hhhd) → %d% m", name, value, overwrite, rc);
return rc;
}
IGNORE_LEAKS(setenv)

View file

@ -27,9 +27,9 @@
#include "libc/errno.h"
#include "libc/fmt/wintime.internal.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/leaky.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/nt/accounting.h"
#include "libc/nt/enum/processaccess.h"
@ -283,7 +283,7 @@ textwindows struct Proc *__proc_new(void) {
}
if (!proc) {
if (_weaken(malloc)) {
proc = _weaken(malloc)(sizeof(struct Proc));
proc = may_leak(_weaken(malloc)(sizeof(struct Proc)));
} else {
enomem();
return 0;
@ -297,8 +297,6 @@ textwindows struct Proc *__proc_new(void) {
return proc;
}
IGNORE_LEAKS(__proc_new)
/**
* Adds process to active list.
*

View file

@ -17,7 +17,12 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/cxaatexit.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/gc.internal.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls.h"
#include "third_party/nsync/wait_s.internal.h"
struct Dtor {
void *fun;
@ -27,18 +32,58 @@ struct Dtor {
static _Thread_local struct Dtor *__cxa_thread_atexit_list;
static void __cxa_thread_unkey(struct CosmoTib *tib) {
void *val;
int i, j, gotsome;
pthread_key_dtor dtor;
for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; ++j) {
for (gotsome = i = 0; i < PTHREAD_KEYS_MAX; ++i) {
if ((val = tib->tib_keys[i]) &&
(dtor = atomic_load_explicit(_pthread_key_dtor + i,
memory_order_relaxed)) &&
dtor != (pthread_key_dtor)-1) {
gotsome = 1;
tib->tib_keys[i] = 0;
dtor(val);
}
}
if (!gotsome) {
break;
}
}
}
static void _pthread_ungarbage(struct CosmoTib *tib) {
struct Garbages *g;
while ((g = tib->tib_garbages)) {
tib->tib_garbages = 0;
while (g->i--) {
((void (*)(intptr_t))g->p[g->i].fn)(g->p[g->i].arg);
}
_weaken(free)(g->p);
_weaken(free)(g);
}
}
void __cxa_thread_finalize(void) {
struct Dtor *dtor;
while ((dtor = __cxa_thread_atexit_list)) {
__cxa_thread_atexit_list = dtor->next;
((void (*)(void *))dtor->fun)(dtor->arg);
free(dtor);
_weaken(free)(dtor);
}
struct CosmoTib *tib = __get_tls();
__cxa_thread_unkey(tib);
if (tib->tib_nsync)
_weaken(nsync_waiter_destroy)(tib->tib_nsync);
_pthread_ungarbage(tib);
}
int __cxa_thread_atexit_impl(void *fun, void *arg, void *dso_symbol) {
struct Dtor *dtor;
if (!(dtor = malloc(sizeof(struct Dtor))))
if (!_weaken(malloc))
return -1;
if (!(dtor = _weaken(malloc)(sizeof(struct Dtor))))
return -1;
dtor->fun = fun;
dtor->arg = arg;

View file

@ -40,14 +40,10 @@ wontreturn void exit(int exitcode) {
STRACE("exit(%d)", exitcode);
// call thread local c++ object destructors
if (_weaken(__cxa_thread_finalize)) {
_weaken(__cxa_thread_finalize)();
}
__cxa_thread_finalize();
// call atexit() and __cxa_atexit() destructors
if (_weaken(__cxa_finalize)) {
_weaken(__cxa_finalize)(NULL);
}
__cxa_finalize(NULL);
// call __destructor__ and finiarray destructors
const uintptr_t *p;

View file

@ -109,7 +109,6 @@ void __paginate_file(int, const char *) libcesque;
void _weakfree(void *) libcesque;
void *_mapanon(size_t) attributeallocsize((1)) mallocesque libcesque;
void *_mapshared(size_t) attributeallocsize((1)) mallocesque libcesque;
void CheckForMemoryLeaks(void) libcesque;
void CheckForFileLeaks(void) libcesque;
void __enable_threads(void) libcesque;
void __oom_hook(size_t) libcesque;

View file

@ -33,6 +33,7 @@
#include "libc/intrin/weaken.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/runtime/runtime.h"

View file

@ -110,8 +110,6 @@ void _pthread_lock(void) libcesque;
void _pthread_onfork_child(void) libcesque;
void _pthread_onfork_parent(void) libcesque;
void _pthread_onfork_prepare(void) libcesque;
void _pthread_ungarbage(void) libcesque;
void _pthread_unkey(struct CosmoTib *) libcesque;
void _pthread_unlock(void) libcesque;
void _pthread_unref(struct PosixThread *) libcesque;
void _pthread_unwind(struct PosixThread *) libcesque;

View file

@ -23,7 +23,6 @@
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/leaky.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/proc/proc.internal.h"
@ -100,5 +99,3 @@ int _pthread_atfork(atfork_f prepare, atfork_f parent, atfork_f child) {
rc = 0;
return rc;
}
IGNORE_LEAKS(_pthread_atfork)

View file

@ -44,27 +44,6 @@ void _pthread_unwind(struct PosixThread *pt) {
}
}
void _pthread_unkey(struct CosmoTib *tib) {
void *val;
int i, j, gotsome;
pthread_key_dtor dtor;
for (j = 0; j < PTHREAD_DESTRUCTOR_ITERATIONS; ++j) {
for (gotsome = i = 0; i < PTHREAD_KEYS_MAX; ++i) {
if ((val = tib->tib_keys[i]) &&
(dtor = atomic_load_explicit(_pthread_key_dtor + i,
memory_order_relaxed)) &&
dtor != (pthread_key_dtor)-1) {
gotsome = 1;
tib->tib_keys[i] = 0;
dtor(val);
}
}
if (!gotsome) {
break;
}
}
}
/**
* Terminates current POSIX thread.
*
@ -111,14 +90,7 @@ wontreturn void pthread_exit(void *rc) {
// free resources
_pthread_unwind(pt);
if (_weaken(__cxa_thread_finalize)) {
_weaken(__cxa_thread_finalize)();
}
_pthread_unkey(tib);
if (tib->tib_nsync) {
nsync_waiter_destroy(tib->tib_nsync);
}
_pthread_ungarbage();
__cxa_thread_finalize();
_pthread_decimate();
// run atexit handlers if orphaned thread

View file

@ -1,35 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/mem/mem.h"
#include "libc/nexgen32e/gc.internal.h"
#include "libc/thread/tls.h"
void _pthread_ungarbage(void) {
struct Garbages *g;
struct CosmoTib *tib;
tib = __get_tls();
while ((g = tib->tib_garbages)) {
tib->tib_garbages = 0;
while (g->i--) {
((void (*)(intptr_t))g->p[g->i].fn)(g->p[g->i].arg);
}
free(g->p);
free(g);
}
}

View file

@ -14,9 +14,7 @@ TEST_CTL_TESTS = $(TEST_CTL_COMS:%=%.ok)
TEST_CTL_DIRECTDEPS = \
CTL \
LIBC_INTRIN \
LIBC_LOG \
LIBC_MEM \
THIRD_PARTY_LIBCXX \
TEST_CTL_DEPS := \
$(call uniq,$(foreach x,$(TEST_CTL_DIRECTDEPS),$($(x))))

223
test/ctl/map_test.cc Normal file
View file

@ -0,0 +1,223 @@
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
//
// Copyright 2024 Justine Alexandra Roberts Tunney
//
// Permission to use, copy, modify, and/or distribute this software for
// any purpose with or without fee is hereby granted, provided that the
// above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "ctl/map.h"
#include "ctl/string.h"
#include "libc/mem/leaks.h"
// #include <map>
// #include <string>
// #define ctl std
int
main()
{
{
ctl::map<int, double> s;
if (!s.empty())
return 1;
if (s.size())
return 2;
s[1] = 10;
s[2] = 20;
s[3] = 3.14;
if (s[1] != 10)
return 3;
if (s[2] != 20)
return 4;
if (s[3] != 3.14)
return 5;
}
{
ctl::map<int, double> m;
if (!m.empty())
return 1;
if (m.size() != 0)
return 2;
m[1] = 10.5;
m[2] = 20.7;
m[3] = 3.14;
if (m.empty())
return 3;
if (m.size() != 3)
return 4;
if (m[1] != 10.5)
return 5;
if (m[2] != 20.7)
return 6;
if (m[3] != 3.14)
return 7;
}
{
ctl::map<ctl::string, int> m;
m["one"] = 1;
m["two"] = 2;
m["three"] = 3;
int sum = 0;
for (const auto& pair : m) {
sum += pair.second;
}
if (sum != 6)
return 8;
auto it = m.find("two");
if (it == m.end())
return 9;
if (it->second != 2)
return 10;
it = m.find("four");
if (it != m.end())
return 11;
}
{
ctl::map<int, ctl::string> m = { { 1, "one" },
{ 2, "two" },
{ 3, "three" } };
if (m.size() != 3)
return 12;
if (m[2] != "two")
return 13;
auto result = m.insert({ 4, "four" });
if (!result.second)
return 14;
if (result.first->second != "four")
return 15;
result = m.insert({ 2, "deux" });
if (result.second)
return 16;
if (result.first->second != "two")
return 17;
}
{
ctl::map<int, double> m1 = { { 1, 1.1 }, { 2, 2.2 }, { 3, 3.3 } };
ctl::map<int, double> m2 = { { 1, 1.1 }, { 2, 2.2 }, { 3, 3.3 } };
ctl::map<int, double> m3 = { { 1, 1.1 }, { 2, 2.2 }, { 4, 4.4 } };
if (!(m1 == m2))
return 18;
if (m1 != m2)
return 19;
if (m1 == m3)
return 20;
if (!(m1 != m3))
return 21;
}
{
ctl::map<int, ctl::string> m;
m[1] = "one";
m[2] = "two";
m[3] = "three";
if (m.count(2) != 1)
return 22;
if (m.count(4) != 0)
return 23;
m.erase(2);
if (m.size() != 2)
return 24;
if (m.count(2) != 0)
return 25;
}
{
ctl::map<int, double> m = { { 1, 1.1 }, { 3, 3.3 }, { 5, 5.5 } };
auto lower = m.lower_bound(2);
if (lower->first != 3)
return 26;
auto upper = m.upper_bound(3);
if (upper->first != 5)
return 27;
auto range = m.equal_range(3);
if (range.first->first != 3 || range.second->first != 5)
return 28;
}
{
ctl::map<int, ctl::string> m = {
{ 1, "one" }, { 2, "two" }, { 3, "three" }, { 4, "four" }
};
// Test reverse iteration
auto rit = m.rbegin();
if (rit->first != 4 || rit->second != "four")
return 29;
++rit;
if (rit->first != 3 || rit->second != "three")
return 30;
// Test reverse iteration with const_reverse_iterator
auto crit = m.crbegin();
if (crit->first != 4 || crit->second != "four")
return 31;
++crit;
++crit;
if (crit->first != 2 || crit->second != "two")
return 32;
// Test rend()
rit = m.rbegin();
int count = 0;
while (rit != m.rend()) {
++rit;
++count;
}
if (count != 4)
return 33;
// Test reverse iteration with range-based for loop
ctl::string result;
for (auto it = m.rbegin(); it != m.rend(); ++it) {
result += it->second;
}
if (result != "fourthreetwoone")
return 34;
// Test modification through reverse iterator
rit = m.rbegin();
rit->second = "quatre";
if (m[4] != "quatre")
return 35;
// Test comparison of reverse iterators
auto rit1 = m.rbegin();
auto rit2 = m.rbegin();
++rit2;
if (!(*rit1 > *rit2))
return 36;
}
CheckForMemoryLeaks();
}

View file

@ -17,11 +17,10 @@
// PERFORMANCE OF THIS SOFTWARE.
#include "ctl/optional.h"
#include "libc/mem/leaks.h"
#include "ctl/string.h"
#include "libc/runtime/runtime.h"
// #include <optional>
// #include <string>
// #define ctl std
@ -131,5 +130,4 @@ main()
}
CheckForMemoryLeaks();
return 0;
}

306
test/ctl/set_test.cc Normal file
View file

@ -0,0 +1,306 @@
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
//
// Copyright 2024 Justine Alexandra Roberts Tunney
//
// Permission to use, copy, modify, and/or distribute this software for
// any purpose with or without fee is hereby granted, provided that the
// above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "ctl/set.h"
#include "libc/mem/leaks.h"
// #include <set>
// #define ctl std
// #define check() size()
int
rand32(void)
{
/* Knuth, D.E., "The Art of Computer Programming," Vol 2,
Seminumerical Algorithms, Third Edition, Addison-Wesley, 1998,
p. 106 (line 26) & p. 108 */
static unsigned long long lcg = 1;
lcg *= 6364136223846793005;
lcg += 1442695040888963407;
return lcg >> 32;
}
int
main()
{
{
// Test construction and basic operations
ctl::set<int> s;
if (!s.empty())
return 1;
if (s.size() != 0)
return 2;
s.insert(1);
s.insert(2);
s.insert(3);
if (s.size() != 3)
return 3;
if (s.count(2) != 1)
return 4;
if (s.count(4) != 0)
return 5;
}
{
// Test insertion and find
ctl::set<int> s;
auto result = s.insert(5);
if (!result.second || *result.first != 5)
return 6;
result = s.insert(5); // Duplicate insertion
if (result.second || *result.first != 5)
return 7;
auto it = s.find(5);
if (it == s.end() || *it != 5)
return 8;
it = s.find(6);
if (it != s.end())
return 9;
s.check();
}
{
// Test erase
ctl::set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(5);
s.erase(3);
if (s.size() != 4 || s.count(3) != 0)
return 10;
auto it = s.find(2);
s.erase(it);
if (s.size() != 3 || s.count(2) != 0)
return 11;
}
{
// Test clear
ctl::set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(5);
s.clear();
if (!s.empty() || s.size() != 0)
return 12;
}
{
// Test comparison operators
ctl::set<int> s1{ 1, 2, 3 };
ctl::set<int> s2{ 1, 2, 3 };
ctl::set<int> s3{ 1, 2, 3, 4 };
if (!(s1 == s2))
return 13;
if (s1 != s2)
return 14;
if (!(s1 < s3))
return 15;
if (s3 <= s1)
return 16;
}
{
// Test iterator functionality
ctl::set<int> s{ 5, 3, 1, 4, 2 };
int prev = 0;
for (const auto& value : s) {
if (value <= prev)
return 17;
prev = value;
s.check();
}
}
{
// Test lower_bound and upper_bound
ctl::set<int> s{ 1, 3, 5, 7, 9 };
auto lower = s.lower_bound(4);
auto upper = s.upper_bound(4);
if (*lower != 5 || *upper != 5)
return 18;
s.check();
}
{
// Test emplace
ctl::set<ctl::pair<int, int>> s;
auto result = s.emplace(1, 2);
if (!result.second || result.first->first != 1 ||
result.first->second != 2)
return 19;
s.check();
}
{
// Test insertion and size
ctl::set<int> s;
for (int i = 0; i < 1000; ++i)
s.insert(i);
if (s.size() != 1000)
return 20;
s.check();
}
{
// Test duplicate insertions
ctl::set<int> s;
for (int i = 0; i < 100; ++i) {
s.insert(i);
s.insert(i); // Duplicate
}
if (s.size() != 100)
return 21;
s.check();
}
{
// Test deletion
ctl::set<int> s;
for (int i = 0; i < 100; ++i)
s.insert(i);
for (int i = 0; i < 50; ++i)
s.erase(i);
if (s.size() != 50 || s.find(0) != s.end() || s.find(99) == s.end())
return 22;
s.check();
}
{
// Test balance after multiple insertions
ctl::set<int> s;
for (int i = 0; i < 1000; ++i) {
s.insert(i);
s.check();
}
}
{
// Test balance after multiple insertions and deletions
ctl::set<int> s;
for (int i = 0; i < 20000; ++i) {
if (s.empty() || rand32() % 2 == 0) {
s.insert(rand32() % 500);
} else {
s.erase(rand32() % s.size());
}
s.check();
}
}
{
// Test iterator functionality
ctl::set<int> s;
for (int i = 0; i < 100; ++i)
s.insert(i);
int prev = -1;
for (const auto& val : s) {
if (val <= prev)
return 25;
prev = val;
}
}
{
// Test clear() function
ctl::set<int> s;
for (int i = 0; i < 100; ++i)
s.insert(i);
s.clear();
s.check();
if (!s.empty() || s.size() != 0)
return 26;
}
{
// Test extreme case: insert in descending order
ctl::set<int> s;
for (int i = 1000; i >= 0; --i)
s.insert(i);
if (s.size() != 1001 || *s.begin() != 0 || *s.rbegin() != 1000)
return 27;
}
{
// Test extreme case: insert in ascending order
ctl::set<int> s;
for (int i = 0; i <= 1000; ++i)
s.insert(i);
if (s.size() != 1001 || *s.begin() != 0 || *s.rbegin() != 1000)
return 28;
}
{
// Test lower_bound and upper_bound more extensively
ctl::set<int> s{ 10, 20, 30, 40, 50 };
if (s.lower_bound(25) != s.find(30) || s.upper_bound(25) != s.find(30))
return 29;
if (s.lower_bound(30) != s.find(30) || s.upper_bound(30) != s.find(40))
return 30;
if (s.lower_bound(60) != s.end() || s.upper_bound(60) != s.end())
return 31;
}
{
// Test emplace with more complex types
struct TestStruct
{
int a;
double b;
TestStruct(int a, double b) : a(a), b(b)
{
}
bool operator<(const TestStruct& other) const
{
return a < other.a;
}
};
ctl::set<TestStruct> s;
auto result = s.emplace(5, 3.14);
if (!result.second || result.first->a != 5 || result.first->b != 3.14)
return 32;
s.check();
}
{
ctl::set<int> s;
if (s.count(6) != 0)
return 33;
s.insert(6);
s.insert(6);
s.insert(6);
if (s.count(6) != 1)
return 34;
}
CheckForMemoryLeaks();
}

View file

@ -17,10 +17,10 @@
// PERFORMANCE OF THIS SOFTWARE.
#include "ctl/string.h"
#include "libc/mem/leaks.h"
#include <__type_traits/is_same.h>
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
// #include <string>

View file

@ -17,8 +17,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include "ctl/string_view.h"
#include "libc/runtime/runtime.h"
#include "libc/mem/leaks.h"
#include "libc/str/str.h"
// #include <string_view>

View file

@ -20,8 +20,6 @@
#include <__type_traits/is_same.h>
#include "libc/runtime/runtime.h"
// #include <memory>
// #define ctl std

View file

@ -17,6 +17,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include "ctl/vector.h"
#include "libc/mem/leaks.h"
#include <cosmo.h>

View file

@ -19,6 +19,7 @@
#include "libc/fmt/conv.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/symbols.internal.h"

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/mem/alg.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"

View file

@ -9,6 +9,7 @@ LICENSE
LOCAL CHANGES
- Fix bug in dlmalloc_inspect_all()
- Define dlmalloc_requires_more_vespene_gas()
- Make dlmalloc scalable using sched_getcpu()
- Use faster two power roundup for memalign()

View file

@ -25,6 +25,7 @@
#include "third_party/dlmalloc/vespene.internal.h"
#include "libc/thread/tls.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/kprintf.h"
#include "third_party/nsync/mu.h"
#if !IsTiny()
@ -1221,7 +1222,8 @@ static void internal_inspect_all(mstate m,
}
}
if (start < (void*)next) /* skip if all space is bookkeeping */
handler(start, next, used, arg);
if (start != s) /* [jart] fix phantom alloc bug w/ mspace+mmap */
handler(start, next, used, arg);
if (q == top)
break;
q = next;

View file

@ -25,6 +25,8 @@
#include "libc/nexgen32e/x86feature.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
#include "libc/runtime/runtime.h"
#include "libc/intrin/weaken.h"
#include "third_party/dlmalloc/dlmalloc.h"
#if !FOOTERS || !MSPACES
@ -64,11 +66,28 @@ size_t dlbulk_free(void *array[], size_t nelem) {
return 0;
}
struct ThreadedMallocVisitor {
mstate heap;
void (*handler)(void *start, void *end,
size_t used_bytes, void *arg);
void *arg;
};
static void threaded_malloc_visitor(void *start, void *end,
size_t used_bytes, void *arg) {
struct ThreadedMallocVisitor *tmv = arg;
if (start == tmv->heap)
return;
tmv->handler(start, end, used_bytes, tmv->arg);
}
void dlmalloc_inspect_all(void handler(void *start, void *end,
size_t used_bytes, void *callback_arg),
size_t used_bytes, void *arg),
void *arg) {
for (unsigned i = 0; i < g_heapslen; ++i)
mspace_inspect_all(g_heaps[i], handler, arg);
for (unsigned i = 0; i < g_heapslen; ++i) {
struct ThreadedMallocVisitor tmv = {g_heaps[i], handler, arg};
mspace_inspect_all(g_heaps[i], threaded_malloc_visitor, &tmv);
}
}
forceinline mstate get_arena(void) {

View file

@ -50,6 +50,7 @@
#include "third_party/lua/lrepl.h"
#include "third_party/lua/lualib.h"
#include "third_party/lua/lunix.h"
#include "libc/mem/leaks.h"
#include "tool/args/args.h"
__static_yoink("lua_notice");

View file

@ -35,6 +35,7 @@
#include "libc/log/check.h"
#include "libc/macros.internal.h"
#include "libc/mem/gc.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/crc32.h"
#include "libc/proc/posix_spawn.h"

View file

@ -49,6 +49,7 @@
#include "libc/math.h"
#include "libc/mem/alloca.h"
#include "libc/mem/gc.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/crc32.h"
#include "libc/nexgen32e/rdtsc.h"