runtime 1.0.0
Checked CEL programs, owned values, and reusable execution stacks
Loading...
Searching...
No Matches
value.hpp
1// SPDX-License-Identifier: BSL-1.0
2#ifndef CEL_RUNTIME_VALUE_HPP
3#define CEL_RUNTIME_VALUE_HPP
4
5#include <cel/runtime/value_type.hpp>
6
7#include <array>
8#include <bit>
9#include <cassert>
10#include <cstring>
11#include <memory>
12#include <span>
13#include <stdexcept>
14#include <tuple>
15#include <utility>
16#include <vector>
17
18namespace cel {
21
23namespace detail {
24struct owned_payload;
25struct value_access;
27struct array_view {
28 const value_type& element;
29 const std::byte* data;
30 std::size_t size;
31 std::size_t stride;
32};
33template <class T>
34struct concrete_adapter;
35} // namespace detail
36
57// [knowledge] Never byte-relocate public wrappers; only private canonical carriers transfer owners.
58class value {
59 std::unique_ptr<detail::owned_payload> _payload;
60 friend struct detail::value_access;
61 template <class T>
62 friend struct detail::concrete_adapter;
63
65 explicit value(std::unique_ptr<detail::owned_payload> payload);
68 static value from_leaf(value_type type, const void* source);
71 void copy_leaf(const value_type& expected, void* destination) const;
76 static value aggregate(value_type type, std::span<const value> fields);
81 static value array_owned(value_type element, std::span<value> children);
83 static const std::byte* raw_data(const value& owner) noexcept;
86 static detail::array_view array_data(const std::byte* data) noexcept;
90 static value from_concrete(value_type type,
91 const void* source,
92 void (*write)(const void*, const value_type&, std::byte*));
97 static void write_array(const value_type& element,
98 std::byte* destination,
99 std::size_t count,
100 const void* source,
101 void (*write)(const void*, std::size_t, const value_type&, std::byte*));
105 static void destroy_fields(const value_type& type, std::byte* data, std::size_t count) noexcept;
106
107public:
109 value() noexcept;
113 ~value();
117 value(const value& other);
119 value(value&& other) noexcept;
125 value& operator=(const value& other);
128 value& operator=(value&& other) noexcept;
131 const value_type& type() const;
137 template <class T>
138 static value from(const T& source) {
139 return detail::concrete_adapter<std::remove_cv_t<T>>::from(source);
140 }
141
146 template <class T>
147 T as() const {
148 return detail::concrete_adapter<std::remove_cv_t<T>>::as(*this);
149 }
150
156 static value array(value_type element, std::span<const value> elements);
162 static value tuple(std::span<const value> fields);
169 static value record(value_type type, std::span<const value> fields);
175 static value range(value_type type, std::span<const value> endpoints);
179 std::size_t size() const;
184 value at(std::size_t index) const;
191 void set(std::size_t index, const value& replacement);
195 value field(std::string_view name) const;
201 void set_field(std::string_view name, const value& replacement);
202};
203
204namespace detail {
205
208template <class T>
209struct concrete_adapter {
211 static value_type type() { return value_type::leaf<T>(); }
214 static value from(const T& source) { return value::from_leaf(type(), &source); }
217 static T as(const value& source) {
218 std::array<std::byte, sizeof(T)> bytes{};
219 source.copy_leaf(type(), bytes.data());
220 return std::bit_cast<T>(bytes);
221 }
225 static T read(const value_type&, const std::byte* data) {
226 assert(data != nullptr);
227 std::array<std::byte, sizeof(T)> bytes{};
228 std::memcpy(bytes.data(), data, bytes.size());
229 return std::bit_cast<T>(bytes);
230 }
233 static void write(const T& source, const value_type&, std::byte* data) {
234 static_assert(std::is_trivially_copyable_v<T>);
235 std::memcpy(data, &source, sizeof(T));
236 }
237};
238
240template <class T, class Allocator>
241struct concrete_adapter<std::vector<T, Allocator>> {
243 static value_type type() {
244 static const auto descriptor = value_type::array(concrete_adapter<T>::type());
245 return descriptor;
246 }
249 static value from(const std::vector<T, Allocator>& source) {
250 return value::from_concrete(
251 type(), &source, [](const void* object, const value_type& shape, std::byte* data) {
252 write(*static_cast<const std::vector<T, Allocator>*>(object), shape, data);
253 });
254 }
258 static void write(const std::vector<T, Allocator>& source,
259 const value_type& shape,
260 std::byte* data) {
261 value::write_array(shape.element(), data, source.size(), &source,
262 [](const void* object, std::size_t index, const value_type& element,
263 std::byte* target) {
264 const auto& input =
265 *static_cast<const std::vector<T, Allocator>*>(object);
266 concrete_adapter<T>::write(input[index], element, target);
267 });
268 }
271 static std::vector<T, Allocator> as(const value& source) {
272 if (!source.type().same_shape(type())) throw std::invalid_argument("wrong vector type");
273 return read(source.type(), value::raw_data(source));
274 }
278 static std::vector<T, Allocator> read(const value_type&, const std::byte* data) {
279 std::vector<T, Allocator> result;
280 const auto children = value::array_data(data);
281 result.reserve(children.size);
282 // Extract each element through its trusted concrete adapter.
283 for (std::size_t i = 0; i != children.size; ++i)
284 result.push_back(concrete_adapter<T>::read(
285 children.element, children.data ? children.data + i * children.stride : nullptr));
286 return result;
287 }
288};
289
291template <class... T>
292struct concrete_adapter<std::tuple<T...>> {
295 static value_type type() {
296 static const auto descriptor = [] {
297 const std::array<value_type, sizeof...(T)> fields{concrete_adapter<T>::type()...};
298 return value_type::tuple(fields);
299 }();
300 return descriptor;
301 }
304 static value from(const std::tuple<T...>& source) {
305 return value::from_concrete(
306 type(), &source, [](const void* object, const value_type& shape, std::byte* data) {
307 write(*static_cast<const std::tuple<T...>*>(object), shape, data);
308 });
309 }
312 template <std::size_t... I>
313 static void write_fields(const std::tuple<T...>& source,
314 const value_type& shape,
315 std::byte* data,
316 std::index_sequence<I...>) {
317 std::size_t completed = 0;
318 try {
319 ((concrete_adapter<T>::write(std::get<I>(source), shape.fields()[I],
320 data ? data + shape.offsets()[I] : nullptr),
321 ++completed),
322 ...);
323 } catch (...) {
324 value::destroy_fields(shape, data, completed);
325 throw;
326 }
327 }
330 static void write(const std::tuple<T...>& source, const value_type& shape, std::byte* data) {
331 write_fields(source, shape, data, std::index_sequence_for<T...>{});
332 }
335 template <std::size_t... I>
336 static std::tuple<T...> extract(const value_type& shape,
337 const std::byte* data,
338 std::index_sequence<I...>) {
339 return {concrete_adapter<T>::read(shape.fields()[I],
340 data ? data + shape.offsets()[I] : nullptr)...};
341 }
344 static std::tuple<T...> as(const value& source) {
345 if (!source.type().same_shape(type())) throw std::invalid_argument("wrong tuple type");
346 return read(source.type(), value::raw_data(source));
347 }
351 static std::tuple<T...> read(const value_type& shape, const std::byte* data) {
352 return extract(shape, data, std::index_sequence_for<T...>{});
353 }
354};
355
356} // namespace detail
362template <class T>
364 return detail::concrete_adapter<std::remove_cvref_t<T>>::type();
365}
366
368} // namespace cel
369#endif
Shares an immutable, complete semantic type and canonical byte layout. Leaf identity is concrete C++ ...
Definition value_type.hpp:55
static value_type array(value_type element)
Creates an array descriptor retaining the element's complete shape and capabilities....
static value_type leaf()
Returns T's descriptor as an opaque, trivially copyable leaf. Rejects nontrivial T at compile time....
Definition value_type.hpp:93
static value_type tuple(std::span< const value_type > fields)
Creates a tuple descriptor with canonical source-order fields; empty fields describe unit....
std::size_t size() const
Returns array length, fixed aggregate arity, or range endpoint count. Throws std::invalid_argument fo...
T as() const
Returns a concrete copy of the exact requested leaf, vector, or tuple type. Returned vectors and tupl...
Definition value.hpp:147
static value array(value_type element, std::span< const value > elements)
Copies a sequence of elements with the specified complete shape and ownership operations....
void set(std::size_t index, const value &replacement)
Replaces one child with a copy of the exact expected shape and ownership operations....
value() noexcept
Creates source unit without allocating payload storage.
value field(std::string_view name) const
Copies a named record field; throws std::invalid_argument for non-record/missing name....
static value tuple(std::span< const value > fields)
Copies source-order fields into a canonical tuple; empty fields are source unit. Leaves input fields ...
const value_type & type() const
Borrows the complete descriptor until this value is assigned or destroyed. Example: value::from(true)...
static value range(value_type type, std::span< const value > endpoints)
Copies endpoints in source order for the given range descriptor. Throws std::invalid_argument for a n...
static value from(const T &source)
Copies a concrete leaf or recursively nested owning vector/tuple into a runtime value....
Definition value.hpp:138
static value record(value_type type, std::span< const value > fields)
Copies source-order fields into a record of the specified nominal type. Throws std::invalid_argument ...
value at(std::size_t index) const
Copies a source-order child under the class's aggregate and callable copy semantics....
void set_field(std::string_view name, const value &replacement)
Replaces a named record field with a copy of the exact expected shape. Throws std::invalid_argument f...
value_type native_type()
Returns the runtime's complete descriptor for a trusted native value type. Removes cv/reference quali...
Definition value.hpp:363
@ tuple
Stores ordered, canonically laid-out fields.
Definition value_type.hpp:34
Provides byte extents for stack reservation and type descriptors.
Definition callable.hpp:6