Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start a Unicode library implementation #3

Merged
merged 2 commits into from
Jun 15, 2024
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
14 changes: 10 additions & 4 deletions best/base/fwd.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,21 @@ class track_location;
template <typename...>
class tlist;

// best/strings/rune.h
// best/text/rune.h
class rune;

// best::str cannot be forward-declared because that depends on
// best::text cannot be forward-declared because that depends on
// best::encoding being defined.
//
// best/strings/str.h
// best/text/str.h
// template <best::encoding>
// class str;
// class text;

// best/text/utf.h
struct utf8;
struct wtf8;
struct utf16;
struct utf32;

// best/test/test.h
class test;
Expand Down
19 changes: 14 additions & 5 deletions best/base/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,25 @@ inline constexpr bool is_debug() {
/// be a string literal of the form `"-Wmy-diagnostic"`.
#define BEST_IGNORE_GCC_DIAGNOSTIC(W_) BEST_PRAGMA(GCC diagnostic ignored W_)

/// # `BEST_LINK_NAME()`
///
/// Specifies the linker symbol of a particular function declaration, overriding
/// the usual mangling. This should be placed after the argument list.
#define BEST_LINK_NAME(sym_) asm(sym_)

// HACK: Wait for BestFmt.
template <typename Os, typename A, typename B>
Os& operator<<(Os& os, const std::pair<A, B>& pair) {
return os << "(" << pair.first << ", " << pair.second << ")";
}
template <typename Os>
Os& operator<<(Os& os, std::byte b) {
return os << "0x" << std::hex << int(b);
}

template <typename Os, typename A, typename B>
Os& operator<<(Os& os, const std::pair<A, B>& pair)
requires requires {
{ os << pair.first << pair.second };
}
{
return os << "(" << pair.first << ", " << pair.second << ")";
}
} // namespace best

#endif // BEST_BASE_PORT_H_
2 changes: 2 additions & 0 deletions best/container/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ cc_library(
],
deps = [
":option",
"//best/memory:bytes",
],
)

Expand All @@ -142,6 +143,7 @@ cc_library(
"//best/math:bit",
"//best/math:overflow",
"//best/memory:allocator",
"//best/memory:bytes",
"//best/memory:layout",
"//best/meta:concepts",
"//best/meta:init",
Expand Down
2 changes: 1 addition & 1 deletion best/container/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ class object_ptr final {
/// This type wraps any `T` and reproduces its properties. The wrapped `T` can
/// be accessed via `operator*` and `operator->`.
template <typename T>
class object {
class object final {
public:
/// # `object::wrapped_type`
///
Expand Down
8 changes: 6 additions & 2 deletions best/container/option.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,11 @@ class option final {

// TODO: BestFmt
template <typename Os>
friend Os& operator<<(Os& os, const option& opt) {
friend Os& operator<<(Os& os, const option& opt)
requires best::void_type<T> || requires {
{ os << *opt };
}
{
if (!opt.has_value()) {
return os << "none";
} else if constexpr (best::void_type<T>) {
Expand Down Expand Up @@ -600,7 +604,7 @@ Os& operator<<(Os& os, none_t opt) {
inline constexpr best::option<void> VoidOption{best::in_place};

// Forward declare span as soon as possible.
template <best::object_type, best::option<size_t>>
template <best::object_type, best::option<size_t> = best::none>
class span;

/// --- IMPLEMENTATION DETAILS BELOW ---
Expand Down
2 changes: 1 addition & 1 deletion best/container/pun_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ best::test NonTrivial = [](auto& t) {

best::test String = [](auto& t) {
unsafe::in([&](auto u) {
best::pun<best::str, int> s(best::index<0>, "hello...");
best::pun<best::str, int> s(best::index<0>, best::str("hello..."));
t.expect_eq(s.get<0>(u), "hello...");
});
};
Expand Down
166 changes: 93 additions & 73 deletions best/container/span.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "best/container/option.h"
#include "best/log/location.h"
#include "best/math/overflow.h"
#include "best/memory/bytes.h"
#include "best/meta/concepts.h"
#include "best/meta/init.h"
#include "best/meta/ops.h"
Expand Down Expand Up @@ -117,7 +118,9 @@ inline constexpr best::option<size_t> span_extent =
///
/// Unfortunately, it is not possible to make `best::span<T>` work when `T` is
/// not an object type.
template <best::object_type T, best::option<size_t> n = best::none>
template <best::object_type T,
// NOTE: This default is in the fwd decl in option.h.
best::option<size_t> n /* = best::none */>
class span final {
public:
/// Helper type aliases.
Expand Down Expand Up @@ -235,6 +238,25 @@ class span final {
requires is_const
: span(std::data(il), std::size(il), loc) {}

/// # `span::from_nul()`
///
/// Constructs a new span pointing to a NUL-terminated string (i.e., a string
/// of span elements, the last of which is zero).
///
/// If `data` is null, returns an empty span.
///
/// If this is a fixed-with span, this will perform the usual fatal bounds
/// check upon construction.
constexpr static span from_nul(T* data) {
if (data == nullptr) return {data, 0};

auto ptr = data;
while (*ptr++ != T{0})
;

return best::span(data, ptr - data - 1);
}

private:
template <size_t m>
static constexpr best::option<size_t> minus =
Expand Down Expand Up @@ -581,14 +603,14 @@ class span final {
constexpr void copy_from(best::span<const U> src) const
requires(!is_const)
{
size_t to_copy = std::min({size(), src.size()});
if (!std::is_constant_evaluated() && best::same<T, U> &&
best::copyable<T, trivially>) {
std::memcpy(data(), src.data(), to_copy * size_of<T>);
best::copy_bytes(*this, src);
return;
}

unsafe::in([&](auto u) {
size_t to_copy = best::min(size(), src.size());
for (size_t i = 0; i < to_copy; ++i) {
at(u, i) = src.at(u, i);
}
Expand All @@ -602,13 +624,13 @@ class span final {
constexpr void emplace_from(best::span<const U> src) const
requires(!is_const)
{
size_t to_copy = std::min({size(), src.size()});
if (!std::is_constant_evaluated() && best::same<T, U> &&
best::copyable<T, trivially>) {
std::memcpy(data(), src.data(), to_copy * size_of<T>);
best::copy_bytes(*this, src);
return;
}

size_t to_copy = best::min(size(), src.size());
for (size_t i = 0; i < to_copy; ++i) {
(data() + i).copy_from(src.data() + i, false);
}
Expand Down Expand Up @@ -656,7 +678,11 @@ class span final {

// TODO: BestFmt
template <typename Os>
friend Os& operator<<(Os& os, span sp) {
friend Os& operator<<(Os& os, span sp)
requires requires {
{ os << sp[0] };
}
{
os << "[";
bool first = true;
for (auto&& value : sp) {
Expand All @@ -669,69 +695,19 @@ class span final {
}

// All spans are comparable.
template <typename U, best::option<size_t> m>
template <object_type U, best::option<size_t> m>
constexpr bool operator==(best::span<U, m> that) const
requires best::equatable<T, U>
{
if (size() != that.size()) {
return false;
}

if constexpr (best::can_memcmp<T> && best::can_memcmp<U> &&
best::same<const T, const U>) {
return data() == that.data() || // Optimize for the case where we are
// comparing a span to itself!
std::memcmp(data().raw(), that.data().raw(),
size() * size_of<T>) == 0;
}

for (size_t i = 0; i < size(); ++i) {
if (data()[i] != that.data()[i]) {
return false;
}
}

return true;
}

requires best::equatable<T, U>;
template <contiguous R>
constexpr bool operator==(const R& range) const
requires best::equatable<T, decltype(*std::data(range))> && (!is_span<R>)
{
return *this == best::span(range);
}

template <typename U, best::option<size_t> m>
constexpr best::order_type<T, U> operator<=>(best::span<U, m> that) const {
if constexpr (best::can_memcmp<T> && best::can_memcmp<U> &&
best::same<const T, const U>) {
if (data() == that.data()) {
return size() <=> that.size();
}

size_t prefix = std::min({size(), that.size()});
int result =
std::memcmp(data().raw(), that.data().raw(), prefix * size_of<T>);
if (result < 0) {
return std::strong_ordering::less;
} else if (result > 0) {
return std::strong_ordering::greater;
} else {
return size() <=> that.size();
}
}

size_t prefix = std::min({size(), that.size()});

for (size_t i = 0; i < prefix; ++i) {
if (auto result = data()[i] <=> that.data()[i]; result != 0) {
return result;
}
}

return size() <=> that.size();
}

template <object_type U, best::option<size_t> m>
constexpr auto operator<=>(best::span<U, m> that) const
requires best::comparable<T, U>;
template <contiguous R>
constexpr auto operator<=>(const R& range) const
requires best::comparable<T, decltype(*std::data(range))> && (!is_span<R>)
Expand All @@ -747,6 +723,18 @@ class span final {
span_internal::repr<T, n> repr_;
};

template <typename T>
span(T*, size_t) -> span<T>;
template <typename T>
span(best::object_ptr<T>, size_t) -> span<T>;
template <typename T>
span(std::initializer_list<T>) -> span<const T>;
template <contiguous R>
span(R&& r) -> span<std::remove_reference_t<decltype(*std::data(r))>,
best::static_size<R>>;

// --- IMPLEMENTATION DETAILS BELOW ---

template <best::object_type T, best::option<size_t> n>
struct span<T, n>::iter final {
constexpr iter() = default;
Expand Down Expand Up @@ -798,15 +786,44 @@ inline constexpr size_t BestStaticSize(auto, std::array<T, n>*) {
return n;
}

template <typename T>
span(T*, size_t) -> span<T>;
template <typename T>
span(best::object_ptr<T>, size_t) -> span<T>;
template <typename T>
span(std::initializer_list<T>) -> span<const T>;
template <contiguous R>
span(R&& r) -> span<std::remove_reference_t<decltype(*std::data(r))>,
best::static_size<R>>;
template <object_type T, best::option<size_t> n>
template <object_type U, best::option<size_t> m>
constexpr bool span<T, n>::operator==(span<U, m> that) const
requires best::equatable<T, U>
{
if (size() != that.size()) return false;
if constexpr (best::byte_comparable<T, U>) {
return best::equate_bytes(span<T>(*this), span<U>(that));
}

for (size_t i = 0; i < size(); ++i) {
if (data()[i] != that.data()[i]) {
return false;
}
}

return true;
}

template <object_type T, best::option<size_t> n>
template <object_type U, best::option<size_t> m>
constexpr auto span<T, n>::operator<=>(span<U, m> that) const
requires best::comparable<T, U>
{
if constexpr (best::byte_comparable<T, U>) {
return best::order_type<T, U>(
best::compare_bytes(span<T>(*this), span<U>(that)));
}

size_t prefix = best::min(size(), that.size());
for (size_t i = 0; i < prefix; ++i) {
if (auto result = data()[i] <=> that.data()[i]; result != 0) {
return result;
}
}

return best::order_type<T, U>(size() <=> that.size());
}

template <object_type T, option<size_t> n>
constexpr void span<T, n>::shift_within(unsafe u, size_t dst, size_t src,
Expand Down Expand Up @@ -847,7 +864,8 @@ constexpr void span<T, n>::shift_within(unsafe u, size_t dst, size_t src,
if (src + count <= dst || dst + count <= src) {
// Non-overlapping case.
if constexpr (best::relocatable<T, trivially>) {
std::memcpy(data() + dst, data() + src, count * size_of<T>);
best::copy_bytes(at(u, {.start = dst, .count = count}),
at(u, {.start = src, .count = count}));
at(u, {.start = src, .count = count}).destroy_in_place();
return;
}
Expand All @@ -858,7 +876,8 @@ constexpr void span<T, n>::shift_within(unsafe u, size_t dst, size_t src,
} else if (src < dst && dst < src + count) {
// Forward case.
if constexpr (best::relocatable<T, trivially>) {
std::memmove(data() + dst, data() + src, count * size_of<T>);
best::copy_overlapping_bytes(at(u, {.start = dst, .count = count}),
at(u, {.start = src, .count = count}));
at(u, {.start = src, .end = dst}).destroy_in_place();
return;
}
Expand All @@ -878,7 +897,8 @@ constexpr void span<T, n>::shift_within(unsafe u, size_t dst, size_t src,
} else if (dst < src && src < dst + count) {
// Backward case.
if constexpr (best::relocatable<T, trivially>) {
std::memmove(data() + dst, data() + src, count * size_of<T>);
best::copy_overlapping_bytes(at(u, {.start = dst, .count = count}),
at(u, {.start = src, .count = count}));
at(u, {.start = dst + count, .end = src + count}).destroy_in_place();
return;
}
Expand Down
7 changes: 7 additions & 0 deletions best/container/span_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ best::test InitList = [](auto& t) {
cb({1, 2, 3});
};

best::test FromNul = [](auto& t) {
int ints[] = {1, 2, 3, 0, 4, 5, 6, 0};
auto span = best::span<int>::from_nul(ints);
t.expect_eq(span, best::span{1, 2, 3});
t.expect_eq(best::span<int>::from_nul(nullptr), best::span<int>{});
};

best::test Ordering = [](auto& t) {
int32_t ints[] = {1, 2, 3};
int64_t longs[] = {1, 2, 3};
Expand Down
Loading
Loading