Repository navigation
Expand file tree
/
Copy pathvalue_owned_api_tests.cpp
More file actions
156 lines (147 loc) · 7.03 KB
/
Copy pathvalue_owned_api_tests.cpp
File metadata and controls
156 lines (147 loc) · 7.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <teaql/core/value.hpp>
#include <teaql/runtime/context.hpp>
#include <iostream>
using namespace teaql;
static void check(bool condition, const char* message) {
if (!condition) {
throw std::runtime_error(message);
}
}
class FailingResource final : public std::pmr::memory_resource {
public:
bool fail = false;
std::size_t live_bytes = 0;
private:
void* do_allocate(std::size_t bytes, std::size_t alignment) override {
if (fail) {
throw std::bad_alloc{};
}
auto* result = std::pmr::new_delete_resource()->allocate(bytes, alignment);
live_bytes += bytes;
return result;
}
void do_deallocate(void* pointer, std::size_t bytes, std::size_t alignment) override {
live_bytes -= bytes;
std::pmr::new_delete_resource()->deallocate(pointer, bytes, alignment);
}
bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
return this == &other;
}
};
static void local_transfer() {
GlobalContext global;
auto user = global.make_user_context();
auto owner = user->memory_resource();
auto& text = user->make<String>(65536, 'x');
auto* characters = text.data();
auto& text_value = user->make<Value>(std::move(text));
check(text.empty() && text_value.try_text()->data() == characters &&
text_value.try_text()->size() == 65536,
"same-owner String transfers its exact payload without another character allocation");
auto& list = user->make<Value::List>();
list.reserve(128);
list.emplace_back("list payload long enough to allocate");
list.emplace_back(UINT64_MAX);
auto* elements = list.data();
auto capacity = list.capacity();
auto* nested_text = list.front().try_text()->data();
auto& list_value = user->make<Value>(std::move(list));
check(list.empty() && list_value.as_list()->data() == elements &&
list_value.as_list()->capacity() == capacity &&
list_value.as_list()->front().try_text()->data() == nested_text,
"same-owner List transfers capacity, elements and nested strings");
auto& object = user->make<Value::Object>();
object.emplace(String("long key requiring character storage", owner),
Value(owner, "object payload long enough to allocate"));
auto* node = &*object.begin();
auto* key = object.begin()->first.data();
auto& object_value = user->make<Value>(std::move(object));
check(object.empty() && &*object_value.as_object()->begin() == node &&
object_value.as_object()->begin()->first.data() == key,
"same-owner Object transfers nodes and key storage");
user->close();
check(global.live_user_bytes() == 0, "close returns all transferred payload blocks");
}
static void foreign_retention_and_failure() {
FailingResource upstream;
{
GlobalContext global(&upstream);
(void)global.make<Value>(0); // Precharge the object page before payload failure injection.
Value* retained_text = nullptr;
Value* retained_list = nullptr;
Value* retained_object = nullptr;
{
auto user = global.make_user_context();
auto owner = user->memory_resource();
auto& text = user->make<String>(100000, 't');
auto& list = user->make<Value::List>();
list.emplace_back(text);
auto& object = user->make<Value::Object>();
object.emplace(String("large", owner), Value(owner, text));
auto* text_data = text.data();
auto* list_data = list.data();
auto* object_node = &*object.begin();
upstream.fail = true;
for (unsigned kind = 0; kind < 3; ++kind) {
bool failed = false;
try {
if (kind == 0) {
(void)global.make<Value>(std::move(text));
} else if (kind == 1) {
(void)global.make<Value>(std::move(list));
} else {
(void)global.make<Value>(std::move(object));
}
} catch (const std::bad_alloc&) {
failed = true;
}
check(failed && text.size() == 100000 && text.data() == text_data &&
list.size() == 1 && list.data() == list_data && object.size() == 1 &&
&*object.begin() == object_node,
"failed foreign preparation preserves every source");
}
upstream.fail = false;
retained_text = &global.make<Value>(std::move(text));
retained_list = &global.make<Value>(std::move(list));
retained_object = &global.make<Value>(std::move(object));
check(retained_text->try_text()->data() != text_data &&
retained_list->as_list()->data() != list_data &&
&*retained_object->as_object()->begin() != object_node,
"foreign retention owns fresh destination storage");
check(text.size() == 100000 && list.size() == 1 && object.size() == 1,
"foreign relocation retains the existing Value copy contract");
}
check(global.live_user_bytes() == 0 && retained_text->try_text()->size() == 100000 &&
retained_list->as_list()->front().try_text()->size() == 100000 &&
retained_object->as_object()->begin()->second.try_text()->size() == 100000,
"retained payloads survive actual User destruction");
auto destination = global.memory_resource();
check(std::get<String>(retained_text->data).get_allocator().resource() == destination &&
retained_list->as_list()->get_allocator().resource() == destination &&
retained_list->as_list()->front().memory_resource() == destination &&
std::get<String>(retained_list->as_list()->front().data)
.get_allocator()
.resource() == destination &&
retained_object->as_object()->get_allocator().resource() == destination &&
retained_object->as_object()->begin()->first.get_allocator().resource() ==
destination &&
retained_object->as_object()->begin()->second.memory_resource() == destination &&
std::get<String>(retained_object->as_object()->begin()->second.data)
.get_allocator()
.resource() == destination,
"nested containers, keys and values propagate the destination");
}
check(upstream.live_bytes == 0,
"Global destruction returns successful and failed-copy storage");
}
int main() {
try {
auto* previous = std::pmr::set_default_resource(std::pmr::null_memory_resource());
local_transfer();
foreign_retention_and_failure();
std::pmr::set_default_resource(previous);
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}