Skip to content

Commit bc76dd2

Browse files
committed
Add Vec::as_slice and Slice::to_vec conversions
1 parent 961b9a5 commit bc76dd2

10 files changed

+125
-48
lines changed

subdoc/lib/gen/files.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ inline std::filesystem::path construct_html_file_path_for_namespace(
7272
// The namespace path includes the namespace element itself, so drop
7373
// that one.
7474
sus::Slice<const Namespace> short_namespace_path =
75-
element.namespace_path.as_ref()["1.."_r];
75+
element.namespace_path.as_slice()["1.."_r];
7676

7777
std::string file_name = [&]() {
7878
if (element.namespace_name.which() == Namespace::Tag::Global) {

subdoc/lib/gen/generate_function.cc

+8-8
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ void generate_function(HtmlWriter::OpenDiv& section_div,
4747
return_type_link.add_href(
4848
construct_html_file_path(
4949
std::filesystem::path(),
50-
element.return_type_element->namespace_path.as_ref(),
51-
element.return_type_element->record_path.as_ref(),
50+
element.return_type_element->namespace_path.as_slice(),
51+
element.return_type_element->record_path.as_slice(),
5252
element.return_type_element->name)
5353
.string());
5454
}
@@ -80,12 +80,12 @@ void generate_function(HtmlWriter::OpenDiv& section_div,
8080
one_param_link.add_class("type-name");
8181
one_param_link.add_title(p.type_name);
8282
if (p.type_element.is_some()) {
83-
one_param_link.add_href(
84-
construct_html_file_path(std::filesystem::path(),
85-
p.type_element->namespace_path.as_ref(),
86-
p.type_element->record_path.as_ref(),
87-
p.type_element->name)
88-
.string());
83+
one_param_link.add_href(construct_html_file_path(
84+
std::filesystem::path(),
85+
p.type_element->namespace_path.as_slice(),
86+
p.type_element->record_path.as_slice(),
87+
p.type_element->name)
88+
.string());
8989
}
9090
one_param_link.write_text(p.short_type_name);
9191
write_comma = true;

subdoc/lib/gen/generate_namespace.cc

+6-5
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ std::string namespace_display_name(const NamespaceElement& element) noexcept {
3535
// The namespace path includes the namespace we're generating for, so drop
3636
// that one.
3737
sus::Slice<const Namespace> short_namespace_path =
38-
element.namespace_path.as_ref()["1.."_r];
38+
element.namespace_path.as_slice()["1.."_r];
3939

4040
// For display in the html, we use the full path name of the namespace.
4141
return namespace_with_path_to_string(short_namespace_path,
@@ -180,7 +180,7 @@ void generate_namespace(const NamespaceElement& element,
180180
});
181181

182182
generate_namespace_namespaces(mref(namespace_div), element,
183-
sorted.as_ref());
183+
sorted.as_slice());
184184
}
185185

186186
{
@@ -210,9 +210,9 @@ void generate_namespace(const NamespaceElement& element,
210210
return a.at<1>() <=> b.at<1>();
211211
});
212212

213-
generate_namespace_records(mref(namespace_div), element, classes.as_ref(),
213+
generate_namespace_records(mref(namespace_div), element, classes.as_slice(),
214214
RecordType::Class);
215-
generate_namespace_records(mref(namespace_div), element, unions.as_ref(),
215+
generate_namespace_records(mref(namespace_div), element, unions.as_slice(),
216216
RecordType::Union);
217217
}
218218

@@ -229,7 +229,8 @@ void generate_namespace(const NamespaceElement& element,
229229
return a.at<1>() <=> b.at<1>();
230230
});
231231

232-
generate_namespace_functions(mref(namespace_div), element, sorted.as_ref());
232+
generate_namespace_functions(mref(namespace_div), element,
233+
sorted.as_slice());
233234
}
234235

235236
// Recurse into namespaces and records.

subdoc/lib/gen/generate_record.cc

+17-16
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void generate_record_overview(HtmlWriter::OpenDiv& record_div,
5050
{
5151
// TODO: This code gets duplicated a lot, share it.
5252

53-
for (const Namespace &n : element.namespace_path.iter().rev()) {
53+
for (const Namespace& n : element.namespace_path.iter().rev()) {
5454
switch (n) {
5555
case Namespace::Tag::Global: break;
5656
case Namespace::Tag::Anonymous: {
@@ -68,7 +68,7 @@ void generate_record_overview(HtmlWriter::OpenDiv& record_div,
6868
break;
6969
}
7070
}
71-
for (std::string_view record_name: element.record_path.iter().rev()) {
71+
for (std::string_view record_name : element.record_path.iter().rev()) {
7272
{
7373
auto record_anchor = full_type_span.open_a();
7474
record_anchor.write_text(record_name);
@@ -157,10 +157,11 @@ void generate_record_fields(HtmlWriter::OpenDiv& record_div,
157157
field_type_link.add_title(fe.type_name);
158158
if (fe.type_element.is_some()) {
159159
field_type_link.add_href(
160-
construct_html_file_path(std::filesystem::path(),
161-
fe.type_element->namespace_path.as_ref(),
162-
fe.type_element->record_path.as_ref(),
163-
fe.type_element->name)
160+
construct_html_file_path(
161+
std::filesystem::path(),
162+
fe.type_element->namespace_path.as_slice(),
163+
fe.type_element->record_path.as_slice(),
164+
fe.type_element->name)
164165
.string());
165166
}
166167
field_type_link.write_text(fe.short_type_name);
@@ -221,14 +222,14 @@ void generate_record_methods(HtmlWriter::OpenDiv& record_div,
221222
void generate_record(const RecordElement& element,
222223
const Options& options) noexcept {
223224
const std::filesystem::path path = construct_html_file_path(
224-
options.output_root, element.namespace_path.as_ref(),
225-
element.record_path.as_ref(), element.name);
225+
options.output_root, element.namespace_path.as_slice(),
226+
element.record_path.as_slice(), element.name);
226227
std::filesystem::create_directories(path.parent_path());
227228
auto html = HtmlWriter(open_file_for_writing(path).unwrap());
228229

229230
{
230231
std::ostringstream title;
231-
for (const Namespace& n: element.namespace_path.iter().rev()) {
232+
for (const Namespace& n : element.namespace_path.iter().rev()) {
232233
switch (n) {
233234
case Namespace::Tag::Global: break;
234235
case Namespace::Tag::Anonymous:
@@ -241,7 +242,7 @@ void generate_record(const RecordElement& element,
241242
break;
242243
}
243244
}
244-
for (std::string_view record_name: element.record_path.iter().rev()) {
245+
for (std::string_view record_name : element.record_path.iter().rev()) {
245246
title << record_name;
246247
title << "::";
247248
}
@@ -285,9 +286,9 @@ void generate_record(const RecordElement& element,
285286
});
286287

287288
generate_record_fields(mref(record_div), element, true,
288-
sorted_static_fields.as_ref());
289+
sorted_static_fields.as_slice());
289290
generate_record_fields(mref(record_div), element, false,
290-
sorted_fields.as_ref());
291+
sorted_fields.as_slice());
291292

292293
sus::Vec<SortedFunctionByName> sorted_static_methods;
293294
sus::Vec<SortedFunctionByName> sorted_methods;
@@ -314,9 +315,9 @@ void generate_record(const RecordElement& element,
314315
});
315316

316317
generate_record_methods(mref(record_div), element, true,
317-
sorted_static_methods.as_ref());
318+
sorted_static_methods.as_slice());
318319
generate_record_methods(mref(record_div), element, false,
319-
sorted_methods.as_ref());
320+
sorted_methods.as_slice());
320321

321322
for (const auto& [key, subrecord] : element.records) {
322323
generate_record(subrecord, options);
@@ -344,8 +345,8 @@ void generate_record_reference(HtmlWriter::OpenDiv& section_div,
344345
name_link.add_class("type-name");
345346
name_link.add_href(
346347
construct_html_file_path(std::filesystem::path(),
347-
element.namespace_path.as_ref(),
348-
element.record_path.as_ref(), element.name)
348+
element.namespace_path.as_slice(),
349+
element.record_path.as_slice(), element.name)
349350
.string());
350351
name_link.write_text(element.name);
351352
}

subdoc/tests/subdoc_gen_test.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class SubDocGenTest : public testing::Test {
4444

4545
auto run_options = subdoc::RunOptions();
4646

47-
auto result = subdoc::run_test(sus::move(content), args.as_ref(),
47+
auto result = subdoc::run_test(sus::move(content), args.as_slice(),
4848
sus::move(run_options));
4949
if (!result.is_ok()) return false;
5050

subdoc/tests/subdoc_test.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class SubDocTest : public testing::Test {
3535
auto args = sus::Vec<std::string>::with_capacity(1u);
3636
args.push(std::string(subdoc::tests::cpp_version_flag(cpp_version_)));
3737

38-
return subdoc::run_test(sus::move(content), args.as_ref(), options);
38+
return subdoc::run_test(sus::move(content), args.as_slice(), options);
3939
}
4040

4141
auto run_code(std::string content) noexcept {

subspace/containers/slice.h

+34-3
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818

1919
#include <algorithm> // Replace std::sort.
2020
#include <concepts>
21+
#include <type_traits>
2122

2223
#include "subspace/assertions/check.h"
2324
#include "subspace/construct/into.h"
2425
#include "subspace/containers/__private/slice_iter.h"
2526
#include "subspace/fn/callable.h"
2627
#include "subspace/iter/iterator_defn.h"
2728
#include "subspace/marker/unsafe.h"
29+
#include "subspace/mem/clone.h"
2830
#include "subspace/num/unsigned_integer.h"
2931
#include "subspace/ops/ord.h"
3032
#include "subspace/ops/range.h"
@@ -36,6 +38,9 @@
3638

3739
namespace sus::containers {
3840

41+
template <class T>
42+
class Vec;
43+
3944
/// A dynamically-sized view into a contiguous sequence, `[T]`.
4045
///
4146
/// Contiguous here means that elements are laid out so that every element is
@@ -44,14 +49,19 @@ namespace sus::containers {
4449
/// Slices are a view into a block of memory represented as a pointer and a
4550
/// length.
4651
template <class T>
47-
class Slice {
52+
class [[sus_trivial_abi]] Slice {
4853
public:
49-
Slice() : Slice(nullptr, 0_usize) {}
54+
static_assert(!std::is_reference_v<T>,
55+
"Slice holds references, so the type parameter can not also be "
56+
"a reference");
57+
58+
constexpr Slice() : Slice(nullptr, 0_usize) {}
5059

5160
static constexpr inline Slice from_raw_parts(::sus::marker::UnsafeFnMarker,
5261
T* data,
5362
::sus::usize len) noexcept {
54-
::sus::check(len.primitive_value <= static_cast<size_t>(isize::MAX_PRIMITIVE));
63+
::sus::check(len.primitive_value <=
64+
static_cast<size_t>(isize::MAX_PRIMITIVE));
5565
return Slice(data, len);
5666
}
5767

@@ -314,13 +324,34 @@ class Slice {
314324
return SliceIterMut<T&>::with(data_, len_);
315325
}
316326

327+
Vec<std::remove_const_t<T>> to_vec() const&
328+
requires(::sus::mem::Clone<T>);
329+
317330
private:
318331
constexpr Slice(T* data, usize len) noexcept : data_(data), len_(len) {}
319332

320333
T* data_;
321334
::sus::usize len_;
335+
336+
sus_class_trivially_relocatable(::sus::marker::unsafe_fn, decltype(data_),
337+
decltype(len_));
338+
339+
// Slice does not satisfy NeverValueField because it requires that the default
340+
// constructor is trivial, but Slice's default constructor needs to initialize
341+
// its fields.
322342
};
323343

344+
template <class T>
345+
Vec<std::remove_const_t<T>> Slice<T>::to_vec() const&
346+
requires(::sus::mem::Clone<T>)
347+
{
348+
auto v = Vec<std::remove_const_t<T>>::with_capacity(len_);
349+
for (::sus::usize i; i < len_; i += 1u) {
350+
v.push(::sus::clone(data_[size_t{i}]));
351+
}
352+
return v;
353+
}
354+
324355
// Implicit for-ranged loop iteration via `Slice::iter()`.
325356
using ::sus::iter::__private::begin;
326357
using ::sus::iter::__private::end;

subspace/containers/slice_unittest.cc

+40
Original file line numberDiff line numberDiff line change
@@ -498,4 +498,44 @@ TEST(Slice, Default) {
498498
EXPECT_TRUE(s.is_empty());
499499
}
500500

501+
TEST(Slice, ToVec) {
502+
sus::Array<i32, 6> array = sus::array(3, 4, 2, 1, 6, 5);
503+
auto slice =
504+
sus::Slice<const i32>::from_raw_parts(unsafe_fn, array.as_ptr(), 6u);
505+
EXPECT_EQ(array.as_ptr(), slice.as_ptr());
506+
sus::Vec<i32> vec = slice.to_vec();
507+
// The Vec is a new allocation.
508+
EXPECT_NE(vec.as_ptr(), slice.as_ptr());
509+
// And it has all the same content, cloned.
510+
EXPECT_EQ(vec.len(), 6u);
511+
EXPECT_EQ(vec[0u], 3);
512+
EXPECT_EQ(vec[1u], 4);
513+
EXPECT_EQ(vec[2u], 2);
514+
EXPECT_EQ(vec[3u], 1);
515+
EXPECT_EQ(vec[4u], 6);
516+
EXPECT_EQ(vec[5u], 5);
517+
518+
// Verify Clone is used, not just Copy.
519+
struct Cloner {
520+
i32 i;
521+
522+
Cloner(i32 i) : i(i) {}
523+
524+
Cloner(Cloner&&) = default;
525+
Cloner& operator=(Cloner&&) = default;
526+
527+
Cloner clone() const noexcept { return Cloner(i + 1); }
528+
};
529+
static_assert(sus::mem::Clone<Cloner>);
530+
static_assert(!sus::mem::Copy<Cloner>);
531+
sus::Array<Cloner, 2> v = sus::array(Cloner(1), Cloner(2));
532+
sus::Vec<Cloner> v2 =
533+
sus::Slice<const Cloner>::from_raw_parts(unsafe_fn, v.as_ptr(), 2u)
534+
.to_vec();
535+
EXPECT_NE(v.as_ptr(), v2.as_ptr());
536+
EXPECT_EQ(v.len(), v2.len());
537+
EXPECT_EQ(v[0u].i + 1, v2[0u].i);
538+
EXPECT_EQ(v[1u].i + 1, v2[1u].i);
539+
}
540+
501541
} // namespace

subspace/containers/vec.h

+11-7
Original file line numberDiff line numberDiff line change
@@ -434,25 +434,25 @@ class Vec {
434434
}
435435

436436
/// #[doc.inherit=[n]sus::[n]containers::[r]Slice::[f]sort]
437-
void sort() { as_mut().sort(); }
437+
void sort() { as_mut_slice().sort(); }
438438

439439
/// #[doc.inherit=[n]sus::[n]containers::[r]Slice::[f]sort_by]
440440
template <class F, int&...,
441441
class R = std::invoke_result_t<F, const T&, const T&>>
442442
requires(::sus::ops::Ordering<R>)
443443
void sort_by(F compare) {
444-
as_mut().sort_by(sus::move(compare));
444+
as_mut_slice().sort_by(sus::move(compare));
445445
}
446446

447447
/// #[doc.inherit=[n]sus::[n]containers::[r]Slice::[f]sort_unstable]
448-
void sort_unstable() { as_mut().sort(); }
448+
void sort_unstable() { as_mut_slice().sort(); }
449449

450450
/// #[doc.inherit=[n]sus::[n]containers::[r]Slice::[f]sort_unstable_by]
451451
template <class F, int&...,
452452
class R = std::invoke_result_t<F, const T&, const T&>>
453453
requires(::sus::ops::Ordering<R>)
454454
void sort_unstable_by(F compare) {
455-
as_mut().sort_by(sus::move(compare));
455+
as_mut_slice().sort_by(sus::move(compare));
456456
}
457457

458458
/// Returns a const pointer to the first element in the vector.
@@ -478,18 +478,18 @@ class Vec {
478478

479479
// Returns a slice that references all the elements of the vector as const
480480
// references.
481-
constexpr Slice<const T> as_ref() const& noexcept {
481+
constexpr Slice<const T> as_slice() const& noexcept {
482482
check(!is_moved_from());
483483
// SAFETY: The `len_` is the number of elements in the Vec, and the pointer
484484
// is to the start of the Vec, so this Slice covers a valid range.
485485
return Slice<const T>::from_raw_parts(::sus::marker::unsafe_fn, storage_,
486486
len_);
487487
}
488-
constexpr Slice<const T> as_ref() && = delete;
488+
constexpr Slice<const T> as_slice() && = delete;
489489

490490
// Returns a slice that references all the elements of the vector as mutable
491491
// references.
492-
constexpr Slice<T> as_mut() & noexcept {
492+
constexpr Slice<T> as_mut_slice() & noexcept {
493493
check(!is_moved_from());
494494
// SAFETY: The `len_` is the number of elements in the Vec, and the pointer
495495
// is to the start of the Vec, so this Slice covers a valid range.
@@ -568,6 +568,10 @@ class Vec {
568568
sus_class_trivially_relocatable_if_types(::sus::marker::unsafe_fn,
569569
decltype(storage_), decltype(len_),
570570
decltype(capacity_));
571+
572+
// Slice does not satisfy NeverValueField because it requires that the default
573+
// constructor is trivial, but Slice's default constructor needs to initialize
574+
// its fields.
571575
};
572576

573577
// Implicit for-ranged loop iteration via `Vec::iter()`.

0 commit comments

Comments
 (0)