diff --git a/AGENTS.md b/AGENTS.md index 19e3658..3e24619 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,6 +7,13 @@ 除非特别说明不允许写任何同步等待代码。 `` +`` +除非特别说明 不允许写任何 事件机制。 +除非特别说明 不允许写任何 事件机制。 +除非特别说明 不允许写任何 事件机制。 +除非特别说明 不允许写任何 事件机制。 +`` + `` 除非特别说明不允许写任何 快照, 任何线程问题先完了都要给我说明 我来审核。 除非特别说明不允许写任何 快照, 任何线程问题先完了都要给我说明 我来审核。 diff --git a/kernel/main.cmake b/kernel/main.cmake index 16ffb66..c341b76 100644 --- a/kernel/main.cmake +++ b/kernel/main.cmake @@ -32,7 +32,7 @@ target_include_directories(Aethera_Kernel PUBLIC "$" "$" "$" - "$" + "$" ) target_compile_features(Aethera_Kernel PUBLIC cxx_std_20) target_link_libraries(Aethera_Kernel PUBLIC @@ -56,19 +56,6 @@ if (Aethera_BUILD_TESTS) ENVIRONMENT "Aethera_ERROR_MODE=exception") list(APPEND Aethera_Kernel_test_targets Aethera_Kernel_exe) endif () -install(TARGETS Aethera_Kernel - EXPORT RenderiveTargets - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") -install(DIRECTORY "${Aethera_Kernel_source_dir}/renderive" - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" PATTERN "*.ipp" PATTERN "*.inl") -install(FILES - "${Aethera_concurrentqueue_dir}/concurrentqueue.h" - "${Aethera_concurrentqueue_dir}/blockingconcurrentqueue.h" - "${Aethera_concurrentqueue_dir}/lightweightsemaphore.h" - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/concurrentqueue-1.0.5") if (Aethera_BUILD_TESTS) add_custom_target(Aethera_Kernel_check COMMAND "${CMAKE_CTEST_COMMAND}" --test-dir "${CMAKE_BINARY_DIR}" diff --git a/kernel/src/kernel/Frame_Policy/Frame_Policy_3D.cpp b/kernel/src/kernel/Frame_Policy/Frame_Policy_3D.cpp index b99b76a..2aae7c9 100644 --- a/kernel/src/kernel/Frame_Policy/Frame_Policy_3D.cpp +++ b/kernel/src/kernel/Frame_Policy/Frame_Policy_3D.cpp @@ -121,10 +121,43 @@ void Frame_Policy_3D::record_frame_submitted( void Frame_Policy_3D::record_frame_completed( std::uint64_t sequence, - std::chrono::steady_clock::duration completion_latency) { - submit({.type = detail::Frame_Policy_Event_Type::frame_completed, - .sequence = sequence, - .duration_ns = duration_nanoseconds_3d(completion_latency)}); + std::chrono::steady_clock::duration completion_latency, + bool prepare_released_after_submission) { + const auto occurred_ns = monotonic_nanoseconds_3d(); + const auto duration_ns = duration_nanoseconds_3d(completion_latency); + auto& state = static_cast( + double_buffer::detail::Internal_Access::pending_state(this)); + auto& common = state.common; + if (common.observation_started_ns == 0) + common.observation_started_ns = occurred_ns; + common.observed_until_ns = std::max( + common.observed_until_ns, occurred_ns); + ++common.completed_frame_count; + if (common.active_frame_count != 0) --common.active_frame_count; + common.last_frame_sequence = sequence; + common.latest_completion_latency_ns = duration_ns; + common.completion_latency_total_ns += duration_ns; + common.maximum_completion_latency_ns = std::max( + common.maximum_completion_latency_ns, duration_ns); + if (common.first_completion_ns == 0) + common.first_completion_ns = occurred_ns; + if (common.last_completion_ns != 0) { + const auto interval = occurred_ns - common.last_completion_ns; + ++common.completion_interval_count; + common.latest_completion_interval_ns = interval; + common.completion_interval_total_ns += interval; + const auto value = static_cast(interval); + common.completion_interval_squared_total_ns2 += value * value; + } + common.last_completion_ns = occurred_ns; + + ++state.gpu_completion_count; + state.last_completed_sequence = sequence; + if (prepare_released_after_submission) + ++state.overlapped_release_count; + else + ++state.completion_gated_release_count; + publish_state(); } bool Frame_Policy_3D::consume_events() { @@ -222,6 +255,10 @@ bool Frame_Policy_3D::consume_events() { } case detail::Frame_Policy_Event_Type::reset: reset_common_counters(common, event.occurred_ns); + state.overlapped_release_count = 0; + state.completion_gated_release_count = 0; + state.gpu_completion_count = 0; + state.last_completed_sequence = 0; break; } } @@ -230,66 +267,4 @@ bool Frame_Policy_3D::consume_events() { return configuration_changed; } -void Frame_Policy_3D::submit_3d(detail::Frame_Policy_3D_Event event) { - submit_stream(std::move(event)); -} - -void Frame_Policy_3D::record_gpu_submitted( - std::uint64_t sequence, bool overlaps_gpu) { - submit_3d({detail::Frame_Policy_3D_Event_Type::submitted, - sequence, overlaps_gpu}); -} - -void Frame_Policy_3D::record_gpu_completed(std::uint64_t sequence) { - submit_3d({detail::Frame_Policy_3D_Event_Type::completed, sequence}); -} - -void Frame_Policy_3D::reset_3d_statistics() { - submit_3d({detail::Frame_Policy_3D_Event_Type::reset}); -} - -bool Frame_Policy_3D::consume_3d_events() { - exchange_stream(); - bool consumed{}; - auto& state = static_cast( - double_buffer::detail::Internal_Access::pending_state(this)); - access_rendering_stream( - [&](std::span events) { - for (const auto& event : events) { - consumed = true; - switch (event.type) { - case detail::Frame_Policy_3D_Event_Type::submitted: - ++state.gpu_submission_count; - ++state.gpu_in_flight; - state.peak_gpu_in_flight = std::max( - state.peak_gpu_in_flight, state.gpu_in_flight); - state.last_submitted_sequence = event.sequence; - if (event.overlaps_gpu) - ++state.overlapped_release_count; - else - ++state.completion_gated_release_count; - break; - case detail::Frame_Policy_3D_Event_Type::completed: - ++state.gpu_completion_count; - if (state.gpu_in_flight != 0) --state.gpu_in_flight; - state.last_completed_sequence = event.sequence; - break; - case detail::Frame_Policy_3D_Event_Type::reset: { - const auto in_flight = state.gpu_in_flight; - state.gpu_submission_count = 0; - state.overlapped_release_count = 0; - state.completion_gated_release_count = 0; - state.gpu_completion_count = 0; - state.gpu_in_flight = in_flight; - state.peak_gpu_in_flight = in_flight; - state.last_submitted_sequence = 0; - state.last_completed_sequence = 0; - break; - } - } - } - }); - if (consumed) publish_state(); - return consumed; -} } diff --git a/kernel/src/kernel/Frame_Policy/Frame_Policy_3D.hpp b/kernel/src/kernel/Frame_Policy/Frame_Policy_3D.hpp index 31a1ea4..c630830 100644 --- a/kernel/src/kernel/Frame_Policy/Frame_Policy_3D.hpp +++ b/kernel/src/kernel/Frame_Policy/Frame_Policy_3D.hpp @@ -3,48 +3,28 @@ #include namespace aethera { -namespace detail { -struct Frame_Policy_3D_Event_Stream; -enum struct Frame_Policy_3D_Event_Type : std::uint8_t { - submitted, - completed, - reset -}; -struct Frame_Policy_3D_Event { - Frame_Policy_3D_Event_Type type{}; - std::uint64_t sequence{}; - bool overlaps_gpu{}; -}; -} /* * 3D 策略是 Frame_Policy 的独立状态层。它复用公共请求/吞吐事实,但不改变 - * 2D Frame_Policy 的默认模式和完成释放语义。GPU 提交与退休由独立 MPMC - * 事件流进入,Plot 的唯一 tick consumer 归并并通过 double-buffer 发布。 + * 2D Frame_Policy 的默认模式和完成释放语义。GPU 后端只把物理事实写入 + * Frame_3D;调用方的唯一策略写者在帧借用结束回调后读取并发布本 State。 */ struct Frame_Policy_3D final : double_buffer::Def< Frame_Policy_3D, double_buffer::Root, double_buffer::Mpmc_Triple_Buffer< detail::Frame_Policy_Event_Stream, - detail::Frame_Policy_Event>, - double_buffer::Mpmc_Triple_Buffer< - detail::Frame_Policy_3D_Event_Stream, - detail::Frame_Policy_3D_Event>> { + detail::Frame_Policy_Event>> { struct Prop : Prev_Prop {}; struct State : Prev_State { Frame_Policy::State common{}; /* 3D 策略拥有的唯一公共节奏状态,不别名 2D 存储。 */ - std::size_t pipeline_capacity{3}; /* Scene 可绑定的物理 3D 帧槽数量。 */ - std::uint64_t gpu_submission_count{}; /* Vulkan 实际提交的累计帧数。 */ std::uint64_t overlapped_release_count{}; /* 提交后即开放下一次 Prepare 的累计帧数。 */ std::uint64_t completion_gated_release_count{}; /* 因共享资源而等 GPU 退休后开放的累计帧数。 */ std::uint64_t gpu_completion_count{}; /* fence 正常或失败退休的累计帧数。 */ - std::uint64_t gpu_in_flight{}; /* 已实际提交且尚未退休的当前帧数。 */ - std::uint64_t peak_gpu_in_flight{}; /* 本统计代次内 gpu_in_flight 峰值。 */ - std::uint64_t last_submitted_sequence{}; /* 最近实际提交的帧序号;未提交时为 0。 */ std::uint64_t last_completed_sequence{}; /* 最近退休的帧序号;未退休时为 0。 */ bool operator==(const State&) const = default; }; struct Private : Prev_Private {}; + static constexpr std::size_t pipeline_capacity{3}; void set_render_enabled(bool enabled); void set_video_enabled(bool enabled); @@ -62,18 +42,14 @@ struct Frame_Policy_3D final : double_buffer::Def< void record_frame_submitted( std::uint64_t sequence, Frame_Request_Source source, std::chrono::steady_clock::duration tick_queue_duration); + /* 仅由调用方的唯一帧策略写者在 Scene 结束借用后调用并直接发布。 */ void record_frame_completed( std::uint64_t sequence, - std::chrono::steady_clock::duration completion_latency); + std::chrono::steady_clock::duration completion_latency, + bool prepare_released_after_submission); [[nodiscard]] bool consume_events(); - void record_gpu_submitted(std::uint64_t sequence, bool overlaps_gpu); - void record_gpu_completed(std::uint64_t sequence); - void reset_3d_statistics(); - [[nodiscard]] bool consume_3d_events(); - private: void submit(detail::Frame_Policy_Event event); - void submit_3d(detail::Frame_Policy_3D_Event event); }; } diff --git a/kernel/src/kernel/event.hpp b/kernel/src/kernel/event.hpp index be5178c..fcb2912 100644 --- a/kernel/src/kernel/event.hpp +++ b/kernel/src/kernel/event.hpp @@ -46,6 +46,10 @@ private: event_type_count> values_; /* Scene 单帧准入域内增量维护的统计器。 */ Event_Statistics_State state_{}; /* 下一次 Scene State 交换时发布的结果。 */ }; +struct Event_Timeline_Time { + std::uint64_t nanoseconds{}; /* 输入生产者单调时间线上的事件发生时刻。 */ + bool operator==(const Event_Timeline_Time&) const = default; +}; /* 所有输入事件的公共业务基类。 */ struct Event { struct Dispatch_Timing { @@ -55,6 +59,7 @@ struct Event { std::uint64_t completed_steady_ns{}; }; explicit Event(Event_Type value); + Event(Event_Type value, Event_Timeline_Time occurred_at); virtual ~Event(); void accept() const noexcept; [[nodiscard]] bool is_accepted() const noexcept; @@ -62,6 +67,7 @@ struct Event { void mark_dispatch_completed() const noexcept; [[nodiscard]] Dispatch_Timing dispatch_timing() const noexcept; Event_Type type; /* 事件种类;构造后保持不变。 */ + const Event_Timeline_Time occurred_at; /* 控制器消费的原始事件时间,不参与内核排队耗时统计。 */ private: friend struct Scene; void observe_with(Event_Statistics_Accumulator* accumulator) noexcept; @@ -115,6 +121,7 @@ struct Wheel_Event_Capability { template struct Basic_Pointer_Event : Event, Pointer_Event_Capability { explicit Basic_Pointer_Event(Event_Type value = Event_Type::pointer_move); + Basic_Pointer_Event(Event_Type value, Event_Timeline_Time occurred_at); Point position{}; /* 事件接收对象局部坐标。 */ Point global_position{}; /* 全局窗口坐标。 */ Mouse_Button button{Mouse_Button::none}; /* 本次按下或释放的按键。 */ @@ -130,6 +137,7 @@ struct Basic_Pointer_Event : Event, Pointer_Event_Capability { template struct Basic_Wheel_Event : Basic_Pointer_Event, Wheel_Event_Capability { Basic_Wheel_Event(); + explicit Basic_Wheel_Event(Event_Timeline_Time occurred_at); double angle_delta_x{}; /* 水平方向滚轮角度增量。 */ double angle_delta_y{}; /* 垂直方向滚轮角度增量。 */ double pixel_delta_x{}; /* 水平方向高精度像素增量。 */ @@ -162,6 +170,7 @@ enum struct Key : std::uint16_t { /* 键盘按下或释放事件。 */ struct Key_Event : Event { explicit Key_Event(Event_Type value); + Key_Event(Event_Type value, Event_Timeline_Time occurred_at); Key key{Key::unknown}; /* 标准化按键。 */ std::uint32_t native_key{}; /* 平台原生按键编码。 */ Keyboard_Modifier modifiers{Keyboard_Modifier::none}; /* 事件发生时的修饰键集合。 */ diff --git a/kernel/src/kernel/event.ipp b/kernel/src/kernel/event.ipp index 0a7e2e5..9ac1cc2 100644 --- a/kernel/src/kernel/event.ipp +++ b/kernel/src/kernel/event.ipp @@ -8,7 +8,10 @@ inline std::uint64_t event_steady_time_ns() noexcept { } } inline Event::Event(Event_Type value) - : type(value), created_steady_ns_(detail::event_steady_time_ns()) {} + : Event(value, Event_Timeline_Time{detail::event_steady_time_ns()}) {} +inline Event::Event(Event_Type value, Event_Timeline_Time value_occurred_at) + : type(value), occurred_at(value_occurred_at), + created_steady_ns_(detail::event_steady_time_ns()) {} inline Event::~Event() = default; inline void Event::accept() const noexcept { accepted = true; } inline bool Event::is_accepted() const noexcept { return accepted; } @@ -38,16 +41,19 @@ inline void Event::observe_with( } constexpr Keyboard_Modifier operator|(Keyboard_Modifier left, Keyboard_Modifier right) noexcept { return static_cast(static_cast(left) | static_cast(right)); } template Basic_Pointer_Event::Basic_Pointer_Event(Event_Type value) : Event(value) {} +template Basic_Pointer_Event::Basic_Pointer_Event(Event_Type value, Event_Timeline_Time value_occurred_at) : Event(value, value_occurred_at) {} template double Basic_Pointer_Event::position_x() const noexcept { return static_cast(position.x); } template double Basic_Pointer_Event::position_y() const noexcept { return static_cast(position.y); } template Mouse_Button Basic_Pointer_Event::pointer_button() const noexcept { return button; } template Mouse_Button_Mask Basic_Pointer_Event::pointer_buttons() const noexcept { return buttons; } template Keyboard_Modifier Basic_Pointer_Event::keyboard_modifiers() const noexcept { return modifiers; } template Basic_Wheel_Event::Basic_Wheel_Event() : Basic_Pointer_Event(Event_Type::wheel) {} +template Basic_Wheel_Event::Basic_Wheel_Event(Event_Timeline_Time value_occurred_at) : Basic_Pointer_Event(Event_Type::wheel, value_occurred_at) {} template double Basic_Wheel_Event::pixel_delta_x_value() const noexcept { return pixel_delta_x; } template double Basic_Wheel_Event::pixel_delta_y_value() const noexcept { return pixel_delta_y; } template double Basic_Wheel_Event::angle_delta_x_value() const noexcept { return angle_delta_x; } template double Basic_Wheel_Event::angle_delta_y_value() const noexcept { return angle_delta_y; } template Basic_Resize_Event::Basic_Resize_Event() : Event(Event_Type::resize) {} inline Key_Event::Key_Event(Event_Type value) : Event(value) {} +inline Key_Event::Key_Event(Event_Type value, Event_Timeline_Time value_occurred_at) : Event(value, value_occurred_at) {} } diff --git a/kernel/src/kernel/frame.cpp b/kernel/src/kernel/frame.cpp index 5980592..a087f4b 100644 --- a/kernel/src/kernel/frame.cpp +++ b/kernel/src/kernel/frame.cpp @@ -118,8 +118,6 @@ Frame_Statistics_Sample Render_Frame::statistics() const { Frame_Trace_Marker::backend_queue_entered, Frame_Trace_Marker::backend_queue_left)); result.set(Frame_Statistic::gpu_submission_ms, interval( Frame_Trace_Marker::gpu_submitted, Frame_Trace_Marker::gpu_completed)); - result.set(Frame_Statistic::callback_ms, interval( - Frame_Trace_Marker::callback_started, Frame_Trace_Marker::callback_finished)); constexpr std::array measurement_statistics{ Frame_Statistic::backend_apply_ms, diff --git a/kernel/src/kernel/frame.hpp b/kernel/src/kernel/frame.hpp index c09e871..7062311 100644 --- a/kernel/src/kernel/frame.hpp +++ b/kernel/src/kernel/frame.hpp @@ -43,8 +43,6 @@ enum struct Frame_Trace_Marker : std::uint8_t { readback_started, readback_finished, scene_render_finished, - callback_started, - callback_finished, frame_ready, count }; diff --git a/kernel/src/kernel/frame_statistics.hpp b/kernel/src/kernel/frame_statistics.hpp index 74295db..38d07f0 100644 --- a/kernel/src/kernel/frame_statistics.hpp +++ b/kernel/src/kernel/frame_statistics.hpp @@ -16,7 +16,6 @@ enum struct Frame_Statistic : std::uint8_t { event_dispatch_ms, backend_queue_ms, gpu_submission_ms, - callback_ms, backend_apply_ms, backend_plan_ms, backend_execute_ms, diff --git a/kernel/src/kernel/render_common.hpp b/kernel/src/kernel/render_common.hpp index 7ead39c..4e8e849 100644 --- a/kernel/src/kernel/render_common.hpp +++ b/kernel/src/kernel/render_common.hpp @@ -103,7 +103,7 @@ struct Task_Runtime_Configuration { std::chrono::milliseconds worker_occupation_limit{100}; /* 单个节点连续非 CPU 等待 Worker 的上限;协作让出区间不计。 */ Task_Overrun_Action worker_overrun_action{ /* 节点超过占用上限后的处置策略。 */ - Task_Overrun_Action::fast_fail + Task_Overrun_Action::warning }; Pmr pmr{}; /* Task Graph 临时结构使用的内存资源。 */ }; diff --git a/kernel/src/test/Event_Timeline_Test.cpp b/kernel/src/test/Event_Timeline_Test.cpp new file mode 100644 index 0000000..550deb9 --- /dev/null +++ b/kernel/src/test/Event_Timeline_Test.cpp @@ -0,0 +1,28 @@ +#include +#include + +namespace aethera::tests { + +struct Event_Test_Point { + double x{}; + double y{}; +}; + +TEST(Event_Timeline, Explicit_Producer_Time_Is_Authoritative) { + constexpr Event_Timeline_Time occurred_at{16'666'667}; + const Basic_Pointer_Event event{ + Event_Type::pointer_move, occurred_at}; + EXPECT_EQ(event.occurred_at, occurred_at); +} + +TEST(Event_Timeline, Zero_Is_A_Valid_Producer_Timeline_Origin) { + const Basic_Wheel_Event event{Event_Timeline_Time{0}}; + EXPECT_EQ(event.occurred_at.nanoseconds, 0U); +} + +TEST(Event_Timeline, Internal_Event_Uses_Its_Creation_Timeline) { + const Event event{Event_Type::show}; + EXPECT_NE(event.occurred_at.nanoseconds, 0U); +} + +} diff --git a/kernel/src/test/Frame_Policy_3D_Test.cpp b/kernel/src/test/Frame_Policy_3D_Test.cpp index 7a08903..da4f00f 100644 --- a/kernel/src/test/Frame_Policy_3D_Test.cpp +++ b/kernel/src/test/Frame_Policy_3D_Test.cpp @@ -5,36 +5,35 @@ namespace { -TEST(frame_policy_3d, publishes_gpu_pipeline_facts_without_changing_common_policy) { +TEST(frame_policy_3d, completed_frame_publishes_frame_attached_gpu_facts) { auto built = aethera::Frame_Policy_3D::Builder< aethera::Frame_Policy_3D>{}.build(); ASSERT_TRUE(built.has_value()); auto policy = std::move(*built); - policy->record_gpu_submitted(11, true); - policy->record_gpu_submitted(12, false); - policy->record_gpu_completed(11); - ASSERT_TRUE(policy->consume_3d_events()); + policy->record_frame_submitted( + 11, aethera::Frame_Request_Source::maximum_rate, + std::chrono::microseconds{40}); + policy->record_frame_submitted( + 12, aethera::Frame_Request_Source::maximum_rate, + std::chrono::microseconds{50}); + static_cast(policy->consume_events()); + policy->record_frame_completed( + 11, std::chrono::microseconds{90}, true); - const auto& pipeline = policy->read_state< + const auto& state = policy->read_state< aethera::Frame_Policy_3D::Base_Tag>(); - EXPECT_EQ(pipeline.pipeline_capacity, 3u); - EXPECT_EQ(pipeline.gpu_submission_count, 2u); - EXPECT_EQ(pipeline.overlapped_release_count, 1u); - EXPECT_EQ(pipeline.completion_gated_release_count, 1u); - EXPECT_EQ(pipeline.gpu_completion_count, 1u); - EXPECT_EQ(pipeline.gpu_in_flight, 1u); - EXPECT_EQ(pipeline.peak_gpu_in_flight, 2u); - EXPECT_EQ(pipeline.last_submitted_sequence, 12u); - EXPECT_EQ(pipeline.last_completed_sequence, 11u); - - const auto& common = pipeline.common; - EXPECT_EQ(common.submitted_frame_count, 0u); - EXPECT_EQ(common.completed_frame_count, 0u); - EXPECT_EQ(common.mode, aethera::Frame_Pacing_Mode::fixed_rate); + EXPECT_EQ(aethera::Frame_Policy_3D::pipeline_capacity, 3u); + EXPECT_EQ(state.overlapped_release_count, 1u); + EXPECT_EQ(state.completion_gated_release_count, 0u); + EXPECT_EQ(state.gpu_completion_count, 1u); + EXPECT_EQ(state.last_completed_sequence, 11u); + EXPECT_EQ(state.common.submitted_frame_count, 2u); + EXPECT_EQ(state.common.completed_frame_count, 1u); + EXPECT_EQ(state.common.active_frame_count, 1u); } -TEST(frame_policy_3d, owns_common_event_stream_without_base_storage_aliasing) { +TEST(frame_policy_3d, common_reset_resets_completed_gpu_statistics) { auto built = aethera::Frame_Policy_3D::Builder< aethera::Frame_Policy_3D>{}.build(); ASSERT_TRUE(built.has_value()); @@ -42,58 +41,33 @@ TEST(frame_policy_3d, owns_common_event_stream_without_base_storage_aliasing) { const auto now = std::chrono::steady_clock::now(); policy->set_mode(aethera::Frame_Pacing_Mode::maximum_rate); - policy->record_request(aethera::Frame_Request_Source::maximum_rate, now); + policy->record_request( + aethera::Frame_Request_Source::maximum_rate, now); policy->record_request_accepted( aethera::Frame_Request_Source::maximum_rate); policy->record_frame_submitted( 31, aethera::Frame_Request_Source::maximum_rate, std::chrono::microseconds{40}); - policy->record_frame_completed(31, std::chrono::microseconds{90}); - policy->record_gpu_submitted(31, true); EXPECT_TRUE(policy->consume_events()); - EXPECT_TRUE(policy->consume_3d_events()); + policy->record_frame_completed( + 31, std::chrono::microseconds{90}, false); - const auto& state = policy->read_state< + const auto& completed = policy->read_state< aethera::Frame_Policy_3D::Base_Tag>(); - EXPECT_EQ(state.common.mode, aethera::Frame_Pacing_Mode::maximum_rate); - EXPECT_EQ(state.common.request_count, 1u); - EXPECT_EQ(state.common.accepted_request_count, 1u); - EXPECT_EQ(state.common.submitted_frame_count, 1u); - EXPECT_EQ(state.common.completed_frame_count, 1u); - EXPECT_EQ(state.common.active_frame_count, 0u); - EXPECT_EQ(state.common.last_frame_sequence, 31u); - EXPECT_EQ(state.gpu_submission_count, 1u); - EXPECT_EQ(state.gpu_in_flight, 1u); + EXPECT_EQ(completed.common.completed_frame_count, 1u); + EXPECT_EQ(completed.completion_gated_release_count, 1u); + EXPECT_EQ(completed.gpu_completion_count, 1u); policy->reset_statistics(); static_cast(policy->consume_events()); const auto& reset = policy->read_state< aethera::Frame_Policy_3D::Base_Tag>(); EXPECT_EQ(reset.common.request_count, 0u); - EXPECT_EQ(reset.gpu_submission_count, 1u); - EXPECT_EQ(reset.gpu_in_flight, 1u); -} - -TEST(frame_policy_3d, reset_preserves_only_current_in_flight_ownership) { - auto built = aethera::Frame_Policy_3D::Builder< - aethera::Frame_Policy_3D>{}.build(); - ASSERT_TRUE(built.has_value()); - auto policy = std::move(*built); - - policy->record_gpu_submitted(21, true); - ASSERT_TRUE(policy->consume_3d_events()); - policy->reset_3d_statistics(); - ASSERT_TRUE(policy->consume_3d_events()); - - const auto& state = policy->read_state< - aethera::Frame_Policy_3D::Base_Tag>(); - EXPECT_EQ(state.pipeline_capacity, 3u); - EXPECT_EQ(state.gpu_submission_count, 0u); - EXPECT_EQ(state.gpu_completion_count, 0u); - EXPECT_EQ(state.gpu_in_flight, 1u); - EXPECT_EQ(state.peak_gpu_in_flight, 1u); - EXPECT_EQ(state.last_submitted_sequence, 0u); - EXPECT_EQ(state.last_completed_sequence, 0u); + EXPECT_EQ(reset.common.completed_frame_count, 0u); + EXPECT_EQ(reset.overlapped_release_count, 0u); + EXPECT_EQ(reset.completion_gated_release_count, 0u); + EXPECT_EQ(reset.gpu_completion_count, 0u); + EXPECT_EQ(reset.last_completed_sequence, 0u); } } diff --git a/kernel/src/test/Task_Runtime_Configuration_Test.cpp b/kernel/src/test/Task_Runtime_Configuration_Test.cpp new file mode 100644 index 0000000..8f672c2 --- /dev/null +++ b/kernel/src/test/Task_Runtime_Configuration_Test.cpp @@ -0,0 +1,14 @@ +#include + +#include "render_common.hpp" + +namespace aethera::test { + +TEST(Task_Runtime_Configuration_Test, Watchdog_Default_Action_Is_Warning) { + const Task_Runtime_Configuration configuration{}; + + EXPECT_EQ(configuration.worker_overrun_action, Task_Overrun_Action::warning); + EXPECT_EQ(configuration.worker_occupation_limit, std::chrono::milliseconds{100}); +} + +} // namespace aethera::test diff --git a/kernel/third_party/GSL-5.0.0/.clang-format b/kernel/third_party/GSL-5.0.0/.clang-format new file mode 100644 index 0000000..08ad16a --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.clang-format @@ -0,0 +1,34 @@ +ColumnLimit: 100 + +UseTab: Never +IndentWidth: 4 +AccessModifierOffset: -4 +NamespaceIndentation: Inner + +BreakBeforeBraces: Custom +BraceWrapping: + AfterNamespace: true + AfterEnum: true + AfterStruct: true + AfterClass: true + SplitEmptyFunction: false + AfterControlStatement: true + AfterFunction: true + AfterUnion: true + BeforeElse: true + + +AlwaysBreakTemplateDeclarations: true +BreakConstructorInitializersBeforeComma: true +ConstructorInitializerAllOnOneLineOrOnePerLine: true +AllowShortBlocksOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true + +PointerAlignment: Left +AlignConsecutiveAssignments: false +AlignTrailingComments: true + +SpaceAfterCStyleCast: true +WhitespaceSensitiveMacros: [GSL_SUPPRESS] diff --git a/kernel/third_party/GSL-5.0.0/.gitattributes b/kernel/third_party/GSL-5.0.0/.gitattributes new file mode 100644 index 0000000..3455dc9 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.gitattributes @@ -0,0 +1 @@ +include/gsl/* linguist-language=C++ diff --git a/kernel/third_party/GSL-5.0.0/.github/ISSUE_TEMPLATE/bug_report.md b/kernel/third_party/GSL-5.0.0/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..be72e49 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'Status: Open, Type: Bug' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +```c++ +#include + +// your repro here: ... +``` + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Spec (please complete the following information):** + - OS: [e.g. Windows] + - Compiler: [e.g. MSVC] + - C++ Version: [e.g. C++20] + +**Additional context** +Add any other context about the problem here. diff --git a/kernel/third_party/GSL-5.0.0/.github/copilot-instructions.md b/kernel/third_party/GSL-5.0.0/.github/copilot-instructions.md new file mode 100644 index 0000000..470e6b8 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.github/copilot-instructions.md @@ -0,0 +1,83 @@ +# GitHub Copilot Instructions for GSL (Guidelines Support Library) + +## Project Overview +This repository contains the Guidelines Support Library (GSL), a Microsoft implementation of types and functions +suggested for use by the C++ Core Guidelines. It's a header-only C++ library with emphasis on safety, +correctness, and zero overhead. + +## Coding Standards + +### General +- Follow C++ Core Guidelines wherever possible +- Use meaningful type, function, and template parameter names +- Keep functions small and focused with clear preconditions/postconditions +- Include comments for complex code, but prefer self-documenting code +- Use the Expects() and Ensures() macros for contract verification + +### Style Guidelines +- Use 4 spaces for indentation (not tabs) +- Maximum line length of 100 characters +- Follow GSL naming conventions (lowercase with underscores) +- Keep templates clean and readable with appropriate spacing +- Use C++14 features since this is the minimum standard supported + +### Error Handling +- Use Expects() for preconditions and Ensures() for postconditions +- Design for fail-fast semantics (std::terminate) on contract violations +- Template constraints should use static_assert or SFINAE +- Don't throw exceptions from basic operations + +### Testing +- Write thorough unit tests for every component using GTest +- Test for all edge cases and error conditions +- Ensure cross-platform compatibility in tests +- Maintain 100% code coverage for changed code + +## Project-Specific Conventions + +### Architecture +- All public types must be in the gsl namespace +- Design for zero overhead abstractions when possible +- Respect the distinction between Owners and Views +- Maintain backward compatibility with existing GSL code + +### Version Control +- Link all PRs to related issues +- Use clear commit messages explaining what and why +- Follow the contribution guidelines documented in CONTRIBUTING.md +- PRs should include appropriate tests with 100% coverage for changed code + +### Documentation +- Document all public APIs with clarity on preconditions and postconditions +- Keep header comments up-to-date +- Include examples for complex functionality in docs/headers.md + +## Technology Stack +- C++14 (minimum) for core implementation +- CMake build system (3.14+) +- Google Test for unit testing +- Support for multiple compilers (MSVC, GCC, Clang) + +## Security Considerations +- Bounds checking is a core principle - enforce it consistently +- Design for safety while minimizing overhead +- Ensure undefined behavior is explicitly detected where possible + +## Performance Guidelines +- Optimize for both safety and performance +- Constexpr-enable functions wherever possible +- Avoid hidden allocations +- Use noexcept appropriately for move operations and other performance-critical functions + +## Cross-Platform Support +- Code must work across: + - Windows (MSVC) + - Linux (GCC, Clang) + - macOS (AppleClang) + +## Copilot Tasks +- You can find the CMake artifacts for C++20 in build-cxx20 and C++14 in build-cxx14. +- Before publishing a PR, verify the following: + - There are no compiler warnings or errors when building the test suite. + - The test suite passes on all supported platforms and compilers. + - The test suite passes for both C++14 and C++20. diff --git a/kernel/third_party/GSL-5.0.0/.github/workflows/clang-format.yml b/kernel/third_party/GSL-5.0.0/.github/workflows/clang-format.yml new file mode 100644 index 0000000..ddb373d --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.github/workflows/clang-format.yml @@ -0,0 +1,39 @@ +name: Code Formatting + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +env: + CLANG_VERSION: "20" + +jobs: + clang-format: + name: Run clang-format + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Install the exact clang-format binary + - name: Install clang-format + run: | + sudo apt install clang-format-${{ env.CLANG_VERSION }} + + # Prints the version of clang-format being used + - name: Log clang-format version + run: clang-format-${{ env.CLANG_VERSION }} --version + + # Runs clang-format over the repository codebase + - name: Check format + run: | + { + find include/gsl -type f + find tests -type f \( -name '*.cpp' -o -name '*.h' \) + } | xargs clang-format-${{ env.CLANG_VERSION }} --dry-run --Werror diff --git a/kernel/third_party/GSL-5.0.0/.github/workflows/cmake/action.yml b/kernel/third_party/GSL-5.0.0/.github/workflows/cmake/action.yml new file mode 100644 index 0000000..d2130cd --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.github/workflows/cmake/action.yml @@ -0,0 +1,33 @@ +name: Composite CMake +inputs: + cmake_preset: + required: true + type: string + extra_cmake_build_args: + required: false + type: string + default: '' + extra_cmake_configure_args: + required: false + type: string + default: '' + extra_ctest_args: + required: false + type: string + default: '' + +runs: + using: composite + steps: + - name: Configure CMake + run: cmake --preset ${{ inputs.cmake_preset }} ${{ inputs.extra_cmake_configure_args }} -DCI_TESTING:BOOL=ON -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -Werror=dev + shell: ${{ env.RUNNER_OS == 'Windows' && 'pwsh' || 'bash' }} + + - name: Build (with preset) + run: cmake --build --preset ${{ inputs.cmake_preset }} ${{ inputs.extra_cmake_build_args }} + shell: ${{ env.RUNNER_OS == 'Windows' && 'pwsh' || 'bash' }} + + - name: Test (with preset) + run: ctest --preset ${{ inputs.cmake_preset }} ${{ inputs.extra_ctest_args }} --output-on-failure --no-compress-output + shell: ${{ env.RUNNER_OS == 'Windows' && 'pwsh' || 'bash' }} + diff --git a/kernel/third_party/GSL-5.0.0/.github/workflows/cmake_find_package.yml b/kernel/third_party/GSL-5.0.0/.github/workflows/cmake_find_package.yml new file mode 100644 index 0000000..5e19c8d --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.github/workflows/cmake_find_package.yml @@ -0,0 +1,25 @@ +name: cmake_find_package +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + cmake-find-package: + name: Build ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, macos-latest ] + steps: + - uses: actions/checkout@v6 + - uses: lukka/get-cmake@latest + with: + cmakeVersion: 3.14.0 + - name: Configure GSL + run: cmake -S . -B build -G "Ninja" -D GSL_TEST=OFF -D CMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/build/install + - name: Install GSL + run: cmake --build build --target install + - name: Test GSL find_package support + run: cmake -S tests/ -B build/tests_find_package -G "Ninja" -D CMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/build/install -D CMAKE_BUILD_TYPE=Release diff --git a/kernel/third_party/GSL-5.0.0/.github/workflows/compilers.yml b/kernel/third_party/GSL-5.0.0/.github/workflows/compilers.yml new file mode 100644 index 0000000..e901391 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.github/workflows/compilers.yml @@ -0,0 +1,132 @@ +name: Compiler Integration Tests + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +# These jobs are correlated with the officially supported compilers +# and toolsets. If you change any versions, please update README.md. + +jobs: + gcc: + strategy: + matrix: + gcc_version: [ 12, 13, 14 ] + build_type: [ Debug, Release ] + cxx_version: [ 14, 17, 20, 23 ] + exclude: + # https://github.com/google/googletest/issues/4232 + # Looks like GoogleTest is not interested in making version 1.14 + # work with gcc-12. + - gcc_version: 12 + cxx_version: 20 + - gcc_version: 12 + cxx_version: 23 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Run CMake (configure, build, test) + uses: ./.github/workflows/cmake + with: + cmake_preset: gcc-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }} + + clang: + strategy: + matrix: + clang_version: [ 16, 17, 18 ] + build_type: [ Debug, Release ] + cxx_version: [ 14, 17, 20, 23 ] + exclude: + # https://github.com/llvm/llvm-project/issues/93734 + # Looks like clang fixed this issue in clang-18, but won't backport + # the fix. + - clang_version: 17 + cxx_version: 23 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Run CMake (configure, build, test) + uses: ./.github/workflows/cmake + with: + cmake_preset: clang-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }} + + linux-sanitizers: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Run CMake with AddressSanitizer and UndefinedBehaviorSanitizer + uses: ./.github/workflows/cmake + with: + cmake_preset: clang-20-debug-asan-ubsan + + xcode: + strategy: + matrix: + xcode_version: [ '26.6' ] + build_type: [ Debug, Release ] + cxx_version: [ 14, 17, 20, 23 ] + runs-on: macos-latest + steps: + - uses: actions/checkout@v6 + + - name: select xcode version + run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode_version }}.app + + - name: Run CMake (configure, build, test) + uses: ./.github/workflows/cmake + with: + cmake_preset: clang-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }} + extra_cmake_configure_args: '-DCMAKE_CXX_FLAGS="-isysroot \"$(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk\""' + + msvc: + strategy: + matrix: + image: [ windows-2022, windows-2025 ] + build_type: [ Debug, Release ] + toolset: [ 'msvc', 'ClangCL' ] + cxx_version: [ 14, 17, 20, 23 ] + include: + # Regular MSVC builds use Ninja (from preset) + - toolset: 'msvc' + generator_override: '' + # ClangCL builds require Visual Studio generator; version depends on image + - image: windows-2022 + toolset: 'ClangCL' + generator_override: '-G "Visual Studio 17 2022" -T ClangCL' + - image: windows-2025 + toolset: 'ClangCL' + generator_override: '-G "Visual Studio 18 2026" -T ClangCL' + runs-on: ${{ matrix.image }} + steps: + - uses: actions/checkout@v6 + - uses: microsoft/setup-msbuild@v3 + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Run CMake (configure, build, test) + uses: ./.github/workflows/cmake + with: + cmake_preset: msvc-${{ matrix.cxx_version }}-${{ matrix.build_type == 'Debug' && 'debug' || 'release' }} + extra_cmake_configure_args: ${{ matrix.generator_override }} + extra_cmake_build_args: ${{ matrix.toolset == 'ClangCL' && format('--config {0}', matrix.build_type) || '' }} + extra_ctest_args: ${{ matrix.toolset == 'ClangCL' && format('-C {0}', matrix.build_type) || '' }} + + windows-sanitizer: + runs-on: windows-latest + steps: + - uses: actions/checkout@v6 + - uses: microsoft/setup-msbuild@v3 + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Run CMake with AddressSanitizer + uses: ./.github/workflows/cmake + with: + cmake_preset: msvc-20-debug-asan diff --git a/kernel/third_party/GSL-5.0.0/.github/workflows/copilot-setup-steps.yml b/kernel/third_party/GSL-5.0.0/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 0000000..d37f633 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,21 @@ +name: "Copilot Setup Steps" + +on: workflow_dispatch + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Install Build Dependencies + run: sudo apt-get update && sudo apt-get install -y clang cmake make + + - name: Configure CMake (C++14) + run: cmake --preset clang-14-debug + + - name: Configure CMake (C++20) + run: cmake --preset clang-20-debug diff --git a/kernel/third_party/GSL-5.0.0/.github/workflows/shell-script-linter.yml b/kernel/third_party/GSL-5.0.0/.github/workflows/shell-script-linter.yml new file mode 100644 index 0000000..c31c511 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.github/workflows/shell-script-linter.yml @@ -0,0 +1,32 @@ +name: Shell script linter + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + clang-format: + name: Run shfmt and shellcheck + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Install the needed binaries + - name: Install shfmt and shellcheck + run: | + sudo apt install shfmt shellcheck + + - name: Check format + run: | + find scripts -type f -name '*.sh' -exec shfmt -l {} \; + + - name: Run shellcheck + run: | + find scripts -type f -name '*.sh' -exec shellcheck {} \; diff --git a/kernel/third_party/GSL-5.0.0/.gitignore b/kernel/third_party/GSL-5.0.0/.gitignore new file mode 100644 index 0000000..4ca21ed --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/.gitignore @@ -0,0 +1,16 @@ +CMakeFiles +build*/ +tests/CMakeFiles +tests/Debug +*.opensdf +*.sdf +tests/*tests.dir +*.vcxproj +*.vcxproj.filters +*.sln +*.tlog +Testing/Temporary/*.* +CMakeCache.txt +*.suo +.vs/ +.vscode/ diff --git a/kernel/third_party/GSL-5.0.0/CMakeLists.txt b/kernel/third_party/GSL-5.0.0/CMakeLists.txt new file mode 100644 index 0000000..6646f52 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.14...3.16) + +project(GSL VERSION 5.0.0 LANGUAGES CXX) + +add_library(GSL INTERFACE) +add_library(Microsoft.GSL::GSL ALIAS GSL) + +# https://cmake.org/cmake/help/latest/variable/PROJECT_IS_TOP_LEVEL.html +string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} PROJECT_IS_TOP_LEVEL) + +option(GSL_INSTALL "Generate and install GSL target" ${PROJECT_IS_TOP_LEVEL}) +option(GSL_TEST "Build and perform GSL tests" ${PROJECT_IS_TOP_LEVEL}) + +# The implementation generally assumes a platform that implements C++14 support +target_compile_features(GSL INTERFACE "cxx_std_14") + +# Setup include directory +add_subdirectory(include) + +target_sources(GSL INTERFACE $) + +if (GSL_TEST) + enable_testing() + add_subdirectory(tests) +endif() + +if (GSL_INSTALL) + include(GNUInstallDirs) + include(CMakePackageConfigHelpers) + + install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/gsl" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + + set(export_name "Microsoft.GSLConfig") + set(namespace "Microsoft.GSL::") + set(cmake_files_install_dir ${CMAKE_INSTALL_DATADIR}/cmake/Microsoft.GSL) + + install(TARGETS GSL EXPORT ${export_name} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + install(EXPORT ${export_name} NAMESPACE ${namespace} DESTINATION ${cmake_files_install_dir}) + export(TARGETS GSL NAMESPACE ${namespace} FILE ${export_name}.cmake) + + set(gls_config_version "${CMAKE_CURRENT_BINARY_DIR}/Microsoft.GSLConfigVersion.cmake") + + write_basic_package_version_file(${gls_config_version} COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT) + + install(FILES ${gls_config_version} DESTINATION ${cmake_files_install_dir}) + + install(FILES GSL.natvis DESTINATION ${cmake_files_install_dir}) +endif() diff --git a/kernel/third_party/GSL-5.0.0/CMakePresets.json b/kernel/third_party/GSL-5.0.0/CMakePresets.json new file mode 100644 index 0000000..da19ca5 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/CMakePresets.json @@ -0,0 +1,504 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "binaryDir": "${sourceDir}/build/${presetName}", + "installDir": "${sourceDir}/install/${presetName}", + "generator": "Ninja", + "cacheVariables": { + "GSL_CXX_STANDARD": "14", + "GSL_TEST": "ON" + } + }, + { + "name": "msvc-base", + "inherits": "base", + "hidden": true, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "cacheVariables": { + "CMAKE_CXX_COMPILER": "cl" + } + }, + { + "name": "gcc-base", + "inherits": "base", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++", + "CMAKE_C_COMPILER": "gcc" + } + }, + { + "name": "clang-base", + "inherits": "base", + "hidden": true, + "cacheVariables": { + "CMAKE_CXX_COMPILER": "clang++", + "CMAKE_C_COMPILER": "clang" + } + }, + { + "name": "msvc-14-debug", + "displayName": "MSVC C++14 Debug", + "inherits": "msvc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "14" + } + }, + { + "name": "msvc-14-release", + "displayName": "MSVC C++14 Release", + "inherits": "msvc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "14" + } + }, + { + "name": "msvc-17-debug", + "displayName": "MSVC C++17 Debug", + "inherits": "msvc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "17" + } + }, + { + "name": "msvc-17-release", + "displayName": "MSVC C++17 Release", + "inherits": "msvc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "17" + } + }, + { + "name": "msvc-20-debug", + "displayName": "MSVC C++20 Debug", + "inherits": "msvc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "20" + } + }, + { + "name": "msvc-20-debug-asan", + "displayName": "MSVC C++20 Debug with AddressSanitizer", + "inherits": "msvc-20-debug", + "cacheVariables": { + "CMAKE_CXX_FLAGS": "/fsanitize=address" + } + }, + { + "name": "msvc-20-release", + "displayName": "MSVC C++20 Release", + "inherits": "msvc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "20" + } + }, + { + "name": "msvc-23-debug", + "displayName": "MSVC C++23 Debug", + "inherits": "msvc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "23" + } + }, + { + "name": "msvc-23-release", + "displayName": "MSVC C++23 Release", + "inherits": "msvc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "23" + } + }, + { + "name": "gcc-14-debug", + "displayName": "GCC C++14 Debug", + "inherits": "gcc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "14" + } + }, + { + "name": "gcc-14-release", + "displayName": "GCC C++14 Release", + "inherits": "gcc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "14" + } + }, + { + "name": "gcc-17-debug", + "displayName": "GCC C++17 Debug", + "inherits": "gcc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "17" + } + }, + { + "name": "gcc-17-release", + "displayName": "GCC C++17 Release", + "inherits": "gcc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "17" + } + }, + { + "name": "gcc-20-debug", + "displayName": "GCC C++20 Debug", + "inherits": "gcc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "20" + } + }, + { + "name": "gcc-20-release", + "displayName": "GCC C++20 Release", + "inherits": "gcc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "20" + } + }, + { + "name": "gcc-23-debug", + "displayName": "GCC C++23 Debug", + "inherits": "gcc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "23" + } + }, + { + "name": "gcc-23-release", + "displayName": "GCC C++23 Release", + "inherits": "gcc-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "23" + } + }, + { + "name": "clang-14-debug", + "displayName": "Clang C++14 Debug", + "inherits": "clang-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "14" + } + }, + { + "name": "clang-14-release", + "displayName": "Clang C++14 Release", + "inherits": "clang-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "14" + } + }, + { + "name": "clang-17-debug", + "displayName": "Clang C++17 Debug", + "inherits": "clang-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "17" + } + }, + { + "name": "clang-17-release", + "displayName": "Clang C++17 Release", + "inherits": "clang-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "17" + } + }, + { + "name": "clang-20-debug", + "displayName": "Clang C++20 Debug", + "inherits": "clang-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "20" + } + }, + { + "name": "clang-20-debug-asan-ubsan", + "displayName": "Clang C++20 Debug with AddressSanitizer and UndefinedBehaviorSanitizer", + "inherits": "clang-20-debug", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + }, + "cacheVariables": { + "CMAKE_CXX_FLAGS": "-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer", + "CMAKE_EXE_LINKER_FLAGS": "-fsanitize=address,undefined" + } + }, + { + "name": "clang-20-release", + "displayName": "Clang C++20 Release", + "inherits": "clang-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "20" + } + }, + { + "name": "clang-23-debug", + "displayName": "Clang C++23 Debug", + "inherits": "clang-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "GSL_CXX_STANDARD": "23" + } + }, + { + "name": "clang-23-release", + "displayName": "Clang C++23 Release", + "inherits": "clang-base", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "GSL_CXX_STANDARD": "23" + } + } + ], + "buildPresets": [ + { + "name": "msvc-14-debug", + "configurePreset": "msvc-14-debug" + }, + { + "name": "msvc-14-release", + "configurePreset": "msvc-14-release" + }, + { + "name": "msvc-17-debug", + "configurePreset": "msvc-17-debug" + }, + { + "name": "msvc-17-release", + "configurePreset": "msvc-17-release" + }, + { + "name": "msvc-20-debug", + "configurePreset": "msvc-20-debug" + }, + { + "name": "msvc-20-debug-asan", + "configurePreset": "msvc-20-debug-asan" + }, + { + "name": "msvc-20-release", + "configurePreset": "msvc-20-release" + }, + { + "name": "msvc-23-debug", + "configurePreset": "msvc-23-debug" + }, + { + "name": "msvc-23-release", + "configurePreset": "msvc-23-release" + }, + { + "name": "gcc-14-debug", + "configurePreset": "gcc-14-debug" + }, + { + "name": "gcc-14-release", + "configurePreset": "gcc-14-release" + }, + { + "name": "gcc-17-debug", + "configurePreset": "gcc-17-debug" + }, + { + "name": "gcc-17-release", + "configurePreset": "gcc-17-release" + }, + { + "name": "gcc-20-debug", + "configurePreset": "gcc-20-debug" + }, + { + "name": "gcc-20-release", + "configurePreset": "gcc-20-release" + }, + { + "name": "gcc-23-debug", + "configurePreset": "gcc-23-debug" + }, + { + "name": "gcc-23-release", + "configurePreset": "gcc-23-release" + }, + { + "name": "clang-14-debug", + "configurePreset": "clang-14-debug" + }, + { + "name": "clang-14-release", + "configurePreset": "clang-14-release" + }, + { + "name": "clang-17-debug", + "configurePreset": "clang-17-debug" + }, + { + "name": "clang-17-release", + "configurePreset": "clang-17-release" + }, + { + "name": "clang-20-debug", + "configurePreset": "clang-20-debug" + }, + { + "name": "clang-20-debug-asan-ubsan", + "configurePreset": "clang-20-debug-asan-ubsan" + }, + { + "name": "clang-20-release", + "configurePreset": "clang-20-release" + }, + { + "name": "clang-23-debug", + "configurePreset": "clang-23-debug" + }, + { + "name": "clang-23-release", + "configurePreset": "clang-23-release" + } + ], + "testPresets": [ + { + "name": "msvc-14-debug", + "configurePreset": "msvc-14-debug" + }, + { + "name": "msvc-14-release", + "configurePreset": "msvc-14-release" + }, + { + "name": "msvc-17-debug", + "configurePreset": "msvc-17-debug" + }, + { + "name": "msvc-17-release", + "configurePreset": "msvc-17-release" + }, + { + "name": "msvc-20-debug", + "configurePreset": "msvc-20-debug" + }, + { + "name": "msvc-20-debug-asan", + "configurePreset": "msvc-20-debug-asan", + "environment": { + "ASAN_OPTIONS": "halt_on_error=1" + } + }, + { + "name": "msvc-20-release", + "configurePreset": "msvc-20-release" + }, + { + "name": "msvc-23-debug", + "configurePreset": "msvc-23-debug" + }, + { + "name": "msvc-23-release", + "configurePreset": "msvc-23-release" + }, + { + "name": "gcc-14-debug", + "configurePreset": "gcc-14-debug" + }, + { + "name": "gcc-14-release", + "configurePreset": "gcc-14-release" + }, + { + "name": "gcc-17-debug", + "configurePreset": "gcc-17-debug" + }, + { + "name": "gcc-17-release", + "configurePreset": "gcc-17-release" + }, + { + "name": "gcc-20-debug", + "configurePreset": "gcc-20-debug" + }, + { + "name": "gcc-20-release", + "configurePreset": "gcc-20-release" + }, + { + "name": "gcc-23-debug", + "configurePreset": "gcc-23-debug" + }, + { + "name": "gcc-23-release", + "configurePreset": "gcc-23-release" + }, + { + "name": "clang-14-debug", + "configurePreset": "clang-14-debug" + }, + { + "name": "clang-14-release", + "configurePreset": "clang-14-release" + }, + { + "name": "clang-17-debug", + "configurePreset": "clang-17-debug" + }, + { + "name": "clang-17-release", + "configurePreset": "clang-17-release" + }, + { + "name": "clang-20-debug", + "configurePreset": "clang-20-debug" + }, + { + "name": "clang-20-debug-asan-ubsan", + "configurePreset": "clang-20-debug-asan-ubsan", + "environment": { + "ASAN_OPTIONS": "detect_leaks=1:halt_on_error=1", + "UBSAN_OPTIONS": "print_stacktrace=1:halt_on_error=1" + } + }, + { + "name": "clang-20-release", + "configurePreset": "clang-20-release" + }, + { + "name": "clang-23-debug", + "configurePreset": "clang-23-debug" + }, + { + "name": "clang-23-release", + "configurePreset": "clang-23-release" + } + ] +} \ No newline at end of file diff --git a/kernel/third_party/GSL-5.0.0/CONTRIBUTING.md b/kernel/third_party/GSL-5.0.0/CONTRIBUTING.md new file mode 100644 index 0000000..e28f534 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/CONTRIBUTING.md @@ -0,0 +1,29 @@ +## Contributing to the Guidelines Support Library + +The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the +[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines). GSL design changes are made only as a result of modifications to the Guidelines. + +GSL is accepting contributions that improve or refine any of the types in this library as well as ports to other platforms. Changes should have an issue +tracking the suggestion that has been approved by the maintainers. Your pull request should include a link to the bug that you are fixing. If you've submitted +a PR, please post a comment in the associated issue to avoid duplication of effort. + +## Legal +You will need to complete a Contributor License Agreement (CLA). Briefly, this agreement testifies that you are granting us and the community permission to +use the submitted change according to the terms of the project's license, and that the work being submitted is under appropriate copyright. + +Please submit a Contributor License Agreement (CLA) before submitting a pull request. You may visit https://cla.microsoft.com to sign digitally. + +## Housekeeping +Your pull request should: + +* Include a description of what your change intends to do +* Be a child commit of a reasonably recent commit in the **main** branch + * Requests need not be a single commit, but should be a linear sequence of commits (i.e. no merge commits in your PR) +* It is desirable, but not necessary, for the tests to pass at each commit. Please see [README.md](./README.md) for instructions to build the test suite. +* Have clear commit messages + * e.g. "Fix issue", "Add tests for type", etc. +* Include appropriate tests + * Tests should include reasonable permutations of the target fix/change + * Include baseline changes with your change + * All changed code must have 100% code coverage +* To avoid line ending issues, set `autocrlf = input` and `whitespace = cr-at-eol` in your git configuration diff --git a/kernel/third_party/GSL-5.0.0/GSL.natvis b/kernel/third_party/GSL-5.0.0/GSL.natvis new file mode 100644 index 0000000..965888c --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/GSL.natvis @@ -0,0 +1,32 @@ + + + + + + {{ invoke = {invoke_}, action = {f_} }} + + invoke_ + f_ + + + + + + {{ extent = {storage_.size_} }} + + + storage_.size_ + storage_.data_ + + + + + + + + value = {*ptr_} + + diff --git a/kernel/third_party/GSL-5.0.0/LICENSE b/kernel/third_party/GSL-5.0.0/LICENSE new file mode 100644 index 0000000..aa58667 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2015 Microsoft Corporation. All rights reserved. + +This code is licensed under the MIT License (MIT). + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/kernel/third_party/GSL-5.0.0/README.md b/kernel/third_party/GSL-5.0.0/README.md new file mode 100644 index 0000000..98902d2 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/README.md @@ -0,0 +1,207 @@ +# GSL: Guidelines Support Library +[![CI](https://github.com/Microsoft/GSL/actions/workflows/compilers.yml/badge.svg)](https://github.com/microsoft/GSL/actions/workflows/compilers.yml?query=branch%3Amain) +[![vcpkg](https://img.shields.io/vcpkg/v/ms-gsl)](https://vcpkg.io/en/package/ms-gsl) + +The Guidelines Support Library (GSL) contains functions and types that are suggested for use by the +[C++ Core Guidelines](https://github.com/isocpp/CppCoreGuidelines) maintained by the [Standard C++ Foundation](https://isocpp.org). +This repo contains Microsoft's implementation of GSL. + +The entire implementation is provided inline in the headers under the [gsl](./include/gsl) directory. The implementation generally assumes a platform that implements C++14 support. + +While some types have been broken out into their own headers (e.g. [gsl/span](./include/gsl/span)), +it is simplest to just include [gsl/gsl](./include/gsl/gsl) and gain access to the entire library. + +> NOTE: We encourage contributions that improve or refine any of the types in this library as well as ports to +other platforms. Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about contributing. + +# Project Code of Conduct +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +# Usage of Third Party Libraries +This project makes use of the [Google Test](https://github.com/google/googletest) testing library. Please see the [ThirdPartyNotices.txt](./ThirdPartyNotices.txt) file for details regarding the licensing of Google Test. + +# Supported features +## Microsoft GSL implements the following from the C++ Core Guidelines: + +Feature | Supported? | Description +-------------------------------------------------------------------------|:----------:|------------- +[**1. Views**][cg-views] | | +[owner](docs/headers.md#user-content-H-pointers-owner) | ☑ | An alias for a raw pointer +[not_null](docs/headers.md#user-content-H-pointers-not_null) | ☑ | Restricts a pointer/smart pointer to hold non-null values +[span](docs/headers.md#user-content-H-span-span) | ☑ | A view over a contiguous sequence of memory. Based on the standardized version of `std::span`, however `gsl::span` enforces bounds checking. +span_p | ☐ | Spans a range starting from a pointer to the first place for which the predicate is true +[basic_zstring](docs/headers.md#user-content-H-zstring) | ☑ | A pointer to a C-string (zero-terminated array) with a templated char type +[zstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `char` +[czstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `const char` +[wzstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `wchar_t` +[cwzstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `const wchar_t` +[u16zstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `char16_t` +[cu16zstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `const char16_t` +[u32zstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `char32_t` +[cu32zstring](docs/headers.md#user-content-H-zstring) | ☑ | An alias to `basic_zstring` with dynamic extent and a char type of `const char32_t` +[**2. Owners**][cg-owners] | | +stack_array | ☐ | A stack-allocated array +dyn_array | ☑ | A heap-allocated array +[**3. Assertions**][cg-assertions] | | +[Expects](docs/headers.md#user-content-H-assert-expects) | ☑ | A precondition assertion; on failure it terminates +[Ensures](docs/headers.md#user-content-H-assert-ensures) | ☑ | A postcondition assertion; on failure it terminates +[**4. Utilities**][cg-utilities] | | +move_owner | ☐ | A helper function that moves one `owner` to the other +[final_action](docs/headers.md#user-content-H-util-final_action) | ☑ | A RAII style class that invokes a functor on its destruction +[finally](docs/headers.md#user-content-H-util-finally) | ☑ | A helper function instantiating [final_action](docs/headers.md#user-content-H-util-final_action) +[GSL_SUPPRESS](docs/headers.md#user-content-H-assert-gsl_suppress) | ☑ | A macro that takes an argument and turns it into `[[gsl::suppress(x)]]` or `[[gsl::suppress("x")]]` depending on the compiler. +[[implicit]] | ☐ | A "marker" to put on single-argument constructors to explicitly make them non-explicit +[index](docs/headers.md#user-content-H-util-index) | ☑ | A type to use for all container and array indexing (currently an alias for `std::ptrdiff_t`) +[narrow](docs/headers.md#user-content-H-narrow-narrow) | ☑ | A checked version of `narrow_cast`; it can throw [narrowing_error](docs/headers.md#user-content-H-narrow-narrowing_error) +[narrow_cast](docs/headers.md#user-content-H-util-narrow_cast) | ☑ | A narrowing cast for values and a synonym for `static_cast` +[narrowing_error](docs/headers.md#user-content-H-narrow-narrowing_error) | ☑ | A custom exception type thrown by [narrow](docs/headers.md#user-content-H-narrow-narrow) +[**5. Concepts**][cg-concepts] | ☐ | + +## The following features do not exist in or have been removed from the C++ Core Guidelines: +Feature | Supported? | Description +-----------------------------------|:----------:|------------- +[strict_not_null](docs/headers.md#user-content-H-pointers-strict_not_null) | ☑ | A stricter version of [not_null](docs/headers.md#user-content-H-pointers-not_null) with explicit constructors +multi_span | ☐ | Deprecated. Multi-dimensional span. +strided_span | ☐ | Deprecated. Support for this type has been discontinued. +basic_string_span | ☐ | Deprecated. Like `span` but for strings with a templated char type +string_span | ☐ | Deprecated. An alias to `basic_string_span` with a char type of `char` +cstring_span | ☐ | Deprecated. An alias to `basic_string_span` with a char type of `const char` +wstring_span | ☐ | Deprecated. An alias to `basic_string_span` with a char type of `wchar_t` +cwstring_span | ☐ | Deprecated. An alias to `basic_string_span` with a char type of `const wchar_t` +u16string_span | ☐ | Deprecated. An alias to `basic_string_span` with a char type of `char16_t` +cu16string_span | ☐ | Deprecated. An alias to `basic_string_span` with a char type of `const char16_t` +u32string_span | ☐ | Deprecated. An alias to `basic_string_span` with a char type of `char32_t` +cu32string_span | ☐ | Deprecated. An alias to `basic_string_span` with a char type of `const char32_t` + +## The following features have been adopted by WG21. They are deprecated in GSL. +Feature | Deprecated Since | Notes +------------------------------------------------------------------|------------------|------ +[unique_ptr](docs/headers.md#user-content-H-pointers-unique_ptr) | C++11 | Use std::unique_ptr instead. +[shared_ptr](docs/headers.md#user-content-H-pointers-shared_ptr) | C++11 | Use std::shared_ptr instead. +[byte](docs/headers.md#user-content-H-byte-byte) | C++17 | Use std::byte instead. +joining_thread | C++20 (Note: Not yet implemented in GSL) | Use std::jthread instead. + +This is based on [CppCoreGuidelines semi-specification](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gsl-guidelines-support-library). + +[cg-views]: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gslview-views +[cg-owners]: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gslowner-ownership-pointers +[cg-assertions]: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gslassert-assertions +[cg-utilities]: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gslutil-utilities +[cg-concepts]: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gslconcept-concepts + +# Quick Start +## Supported Compilers / Toolsets +The GSL officially supports recent major versions of Visual Studio with both MSVC and LLVM, GCC, Clang, and XCode with Apple-Clang. +For each of these major versions, the GSL officially supports C++14, C++17, C++20, and C++23 (when supported by the compiler). +Below is a table showing the versions currently being tested (also see [.github/workflows/compilers.yml](the workflow).) + +Compiler |Toolset Versions Currently Tested +:------- |--: + GCC | 12, 13, 14 + XCode | 14.3.1, 15.4 + Clang | 16, 17, 18 + Visual Studio with MSVC | VS2019, VS2022 + Visual Studio with LLVM | VS2019, VS2022 + +## Building the tests +To build the tests, you will require the following: + +* [CMake](http://cmake.org), version 3.14 or later to be installed and in your PATH. + +These steps assume the source code of this repository has been cloned into a directory named `c:\GSL`. + +1. Create a directory to contain the build outputs for a particular architecture (we name it `c:\GSL\build-x86` in this example). + + cd GSL + md build-x86 + cd build-x86 + +2. Configure CMake to use the compiler of your choice (you can see a list by running `cmake --help`). + + cmake -G "Visual Studio 15 2017" c:\GSL + +3. Build the test suite (in this case, in the Debug configuration, Release is another good choice). + + cmake --build . --config Debug + +4. Run the test suite. + + ctest -C Debug + +All tests should pass - indicating your platform is fully supported and you are ready to use the GSL types! + +## Building GSL - Using vcpkg + +You can download and install GSL using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + vcpkg install ms-gsl + +The GSL port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +## Using the libraries +As the types are entirely implemented inline in headers, there are no linking requirements. + +You can copy the [gsl](./include/gsl) directory into your source tree so it is available +to your compiler, then include the appropriate headers in your program. + +Alternatively set your compiler's *include path* flag to point to the GSL development folder (`c:\GSL\include` in the example above) or installation folder (after running the install). Eg. + +MSVC++ + + /I c:\GSL\include + +GCC/clang + + -I$HOME/dev/GSL/include + +Include the library using: + + #include + +## Usage in CMake + +The library provides a Config file for CMake, once installed it can be found via `find_package`. + +Which, when successful, will add library target called `Microsoft.GSL::GSL` which you can use via the usual +`target_link_libraries` mechanism. + +```cmake +find_package(Microsoft.GSL CONFIG REQUIRED) + +target_link_libraries(foobar PRIVATE Microsoft.GSL::GSL) +``` + +### FetchContent + +If you are using CMake version 3.11+ you can use the official [FetchContent module](https://cmake.org/cmake/help/latest/module/FetchContent.html). +This allows you to easily incorporate GSL into your project. + +```cmake +# NOTE: This example uses CMake version 3.14 (FetchContent_MakeAvailable). +# Since it streamlines the FetchContent process +cmake_minimum_required(VERSION 3.14) + +include(FetchContent) + +FetchContent_Declare(GSL + GIT_REPOSITORY "https://github.com/microsoft/GSL" + GIT_TAG "v5.0.0" + GIT_SHALLOW ON +) + +FetchContent_MakeAvailable(GSL) + +target_link_libraries(foobar PRIVATE Microsoft.GSL::GSL) +``` + +## Debugging visualization support + +For Visual Studio users, the file [GSL.natvis](./GSL.natvis) in the root directory of the repository can be added to your project if you would like more helpful visualization of GSL types in the Visual Studio debugger than would be offered by default. + +## See Also + +For information on [Microsoft Gray Systems Lab (GSL)](https://aka.ms/gsl) of applied data management and system research see . diff --git a/kernel/third_party/GSL-5.0.0/SECURITY.md b/kernel/third_party/GSL-5.0.0/SECURITY.md new file mode 100644 index 0000000..869fdfe --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/kernel/third_party/GSL-5.0.0/ThirdPartyNotices.txt b/kernel/third_party/GSL-5.0.0/ThirdPartyNotices.txt new file mode 100644 index 0000000..552b254 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/ThirdPartyNotices.txt @@ -0,0 +1,41 @@ + +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +GSL: Guidelines Support Library incorporates third party material from the projects listed below. + +------------------------------------------------------------------------------- +Software: Google Test +Owner: Google Inc. +Source URL: github.com/google/googletest +License: BSD 3 - Clause +Text: + Copyright 2008, Google Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------- diff --git a/kernel/third_party/GSL-5.0.0/docs/headers.md b/kernel/third_party/GSL-5.0.0/docs/headers.md new file mode 100644 index 0000000..82cc338 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/docs/headers.md @@ -0,0 +1,1070 @@ +The Guidelines Support Library (GSL) interface is very lightweight and exposed via a header-only library. This document attempts to document all of the headers and their exposed classes and functions. + +Types and functions are exported in the namespace `gsl`. + +See [GSL: Guidelines support library](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#s-gsl) + +# Headers + +- [``](#user-content-H-algorithms) +- [``](#user-content-H-assert) +- [``](#user-content-H-byte) +- [``](#user-content-H-dyn_array) +- [``](#user-content-H-gsl) +- [``](#user-content-H-narrow) +- [``](#user-content-H-pointers) +- [``](#user-content-H-span) +- [``](#user-content-H-span_ext) +- [``](#user-content-H-zstring) +- [``](#user-content-H-util) + +## `` + +This header contains some common algorithms that have been wrapped in GSL safety features. + +- [`gsl::copy`](#user-content-H-algorithms-copy) + +### `gsl::copy` + +```cpp +template +void copy(span src, span dest); +``` + +This function copies the content from the `src` [`span`](#user-content-H-span-span) to the `dest` [`span`](#user-content-H-span-span). It [`Expects`](#user-content-H-assert-expects) +that the destination `span` is at least as large as the source `span`. + +## `` + +This header contains some macros used for contract checking and suppressing code analysis warnings. + +See [GSL.assert: Assertions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-assertions) + +- [`GSL_SUPPRESS`](#user-content-H-assert-gsl_suppress) +- [`Expects`](#user-content-H-assert-expects) +- [`Ensures`](#user-content-H-assert-ensures) + +### `GSL_SUPPRESS` + +This macro can be used to suppress a code analysis warning. + +The core guidelines request tools that check for the rules to respect suppressing a rule by writing +`[[gsl::suppress("tag")]]` or `[[gsl::suppress("tag", justification: "message")]]`. + +Older versions of MSVC (VS 2022 and earlier) only understand `[[gsl::suppress(tag)]]` without the double quotes around `tag`. + +For portable code you can use `GSL_SUPPRESS(tag)`. + +See [In.force: Enforcement](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#inforce-enforcement). + +### `Expects` + +This macro can be used for expressing a precondition. If the precondition is not held, then `std::terminate` will be called. + +See [I.6: Prefer `Expects()` for expressing preconditions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i6-prefer-expects-for-expressing-preconditions) + +### `Ensures` + +This macro can be used for expressing a postcondition. If the postcondition is not held, then `std::terminate` will be called. + +See [I.8: Prefer `Ensures()` for expressing postconditions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i8-prefer-ensures-for-expressing-postconditions) + +## `` + +This header contains the definition of a byte type, implementing `std::byte` before it was standardized into C++17. + +- [`gsl::byte`](#user-content-H-byte-byte) + +### `gsl::byte` + +If `GSL_USE_STD_BYTE` is defined to be `1`, then `gsl::byte` will be an alias to `std::byte`. +If `GSL_USE_STD_BYTE` is defined to be `0`, then `gsl::byte` will be a distinct type that implements the concept of byte. +If `GSL_USE_STD_BYTE` is not defined, then the header file will check if `std::byte` is available (C\+\+17 or higher). If yes, +`gsl::byte` will be an alias to `std::byte`, otherwise `gsl::byte` will be a distinct type that implements the concept of byte. + +⚠ Take care when linking projects that were compiled with different language standards (before C\+\+17 and C\+\+17 or higher). +If you do so, you might want to `#define GSL_USE_STD_BYTE 0` to a fixed value to be sure that both projects use exactly +the same type. Otherwise you might get linker errors. + +See [SL.str.5: Use `std::byte` to refer to byte values that do not necessarily represent characters](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rstr-byte) + +### Non-member functions + +```cpp +template ::value, bool> = true> +constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept; + +template ::value, bool> = true> +constexpr byte operator<<(byte b, IntegerType shift) noexcept; + +template ::value, bool> = true> +constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept; + +template ::value, bool> = true> +constexpr byte operator>>(byte b, IntegerType shift) noexcept; +``` + +Left or right shift a `byte` by a given number of bits. + +```cpp +constexpr byte& operator|=(byte& l, byte r) noexcept; +constexpr byte operator|(byte l, byte r) noexcept; +``` + +Bitwise "or" of two `byte`s. + +```cpp +constexpr byte& operator&=(byte& l, byte r) noexcept; +constexpr byte operator&(byte l, byte r) noexcept; +``` + +Bitwise "and" of two `byte`s. + +```cpp +constexpr byte& operator^=(byte& l, byte r) noexcept; +constexpr byte operator^(byte l, byte r) noexcept; +``` + +Bitwise xor of two `byte`s. + +```cpp +constexpr byte operator~(byte b) noexcept; +``` + +Bitwise negation of a `byte`. Flips all bits. Zeroes become ones, ones become zeroes. + +```cpp +template ::value, bool> = true> +constexpr IntegerType to_integer(byte b) noexcept; +``` + +Convert the given `byte` value to an integral type. + +```cpp +template +constexpr byte to_byte(T t) noexcept; +``` + +Convert the given value to a `byte`. The template requires `T` to be an `unsigned char` so that no data loss can occur. +If you want to convert an integer constant to a `byte` you probably want to call `to_byte()`. + +```cpp +template +constexpr byte to_byte() noexcept; +``` + +Convert the given value `I` to a `byte`. The template requires `I` to be in the valid range 0..255 for a `gsl::byte`. + +## `` + +This header contains an owning dynamically allocated array type whose size is fixed at construction. + +- [`gsl::dyn_array`](#user-content-H-dyn_array-dyn_array) + +### `gsl::dyn_array` + +```cpp +template > +class dyn_array; +``` + +`gsl::dyn_array` owns a contiguous sequence of `T` objects allocated with `Allocator`. +The number of elements is established when the object is constructed and remains unchanged. +It provides bounds-checked element access and checked random-access iterators. + +`gsl::dyn_array` is useful when the number of elements is known only at runtime, but the array should not grow or shrink through container operations. + +#### Member Types + +```cpp +using value_type = T; +using reference = T&; +using const_reference = const T&; +using iterator = details::dyn_array_iterator; +using const_iterator = details::dyn_array_iterator; +using reverse_iterator = std::reverse_iterator; +using const_reverse_iterator = std::reverse_iterator; +using difference_type = std::ptrdiff_t; +using size_type = std::size_t; + +using allocator_type = Allocator; +``` + +#### Member functions + +##### Construct/Copy + +```cpp +explicit constexpr dyn_array(const Allocator& alloc = {}); +``` + +Constructs an empty `dyn_array`. +No elements are allocated and `data()` returns `nullptr`. + +```cpp +constexpr explicit dyn_array(size_type count, const Allocator& alloc = {}); + +constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {}); +``` + +Constructs a `dyn_array` with `count` elements using `alloc`. +The first overload default-constructs each element. +The second overload constructs each element as a copy of `value`. + +```cpp +template +constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {}); +``` + +Constructs a `dyn_array` by copying the elements in the range `[first, last)`. + +```cpp +template +constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {}); +``` + +Constructs a `dyn_array` by copying the elements in `rg`. +This overload is available when container ranges are supported. + +```cpp +constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {}); + +constexpr dyn_array(std::initializer_list init, const Allocator& alloc = {}); +``` + +Constructs a `dyn_array` by copying the elements from another `dyn_array` or from an initializer list. + +```cpp +constexpr auto operator=(const dyn_array& other) -> dyn_array&; + +constexpr dyn_array(dyn_array&&) = delete; +dyn_array& operator=(dyn_array&&) = delete; +``` + +Copy assignment, move assignment, and move construction are explicitly deleted. + +##### Observers + +```cpp +constexpr auto size() const; +constexpr auto empty() const; +constexpr auto max_size() const; +constexpr auto get_allocator() -> Allocator&; +``` + +Returns the number of elements, whether the array is empty, the maximum representable size, or the allocator used by the `dyn_array`. + +##### Element access + +```cpp +constexpr auto operator[](size_type pos) -> reference; +constexpr auto operator[](size_type pos) const -> const_reference; +``` + +Returns a reference to the element at the given index. +[`Expects`](#user-content-H-assert-expects) that `pos` is less than the `dyn_array`'s size. + +```cpp +constexpr auto data(); +constexpr auto data() const -> const T*; +``` + +Returns a pointer to the beginning of the contained data. +If the `dyn_array` is empty, this returns `nullptr`. + +##### Iterators + +```cpp +constexpr auto begin(); +constexpr auto begin() const; +constexpr auto cbegin() const; + +constexpr auto end(); +constexpr auto end() const; +constexpr auto cend() const; +``` + +Returns an iterator to the first element or to one past the last element. + +```cpp +constexpr auto rbegin(); +constexpr auto rbegin() const; +constexpr auto crbegin() const; + +constexpr auto rend(); +constexpr auto rend() const; +constexpr auto crend() const; +``` + +Returns a reverse iterator to the first element of the reversed range or to one past the last element of the reversed range. + +The iterators are random-access iterators and perform bounds checking; they can never be invalidated. +Dereferencing `end()`, moving before `begin()` or past `end()`, or comparing iterators from different arrays violates preconditions. + +##### Comparisons + +```cpp +constexpr auto operator==(const dyn_array& other) const; +constexpr auto operator!=(const dyn_array& other) const; +``` + +Compares two `dyn_array`s by size and element value. + +#### Deduction guides + +```cpp +template ::value_type>> +dyn_array(InputIt, InputIt, + Alloc = {}) -> dyn_array::value_type, Alloc>; + +template >> +dyn_array(std::from_range_t, InputRg&&, + Alloc = {}) -> dyn_array, Alloc>; +``` + +The range deduction guide is available when container ranges are supported. + +## `` + +This header is a convenience header that includes all other [GSL headers](#user-content-H). +Since `` requires exceptions, it will only be included if exceptions are enabled. + +## `` + +This header contains utility functions and classes, for narrowing casts, which require exceptions. The narrowing-related utilities that don't require exceptions are found inside [util](#user-content-H-util). + +See [GSL.util: Utilities](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-utilities) + +- [`gsl::narrowing_error`](#user-content-H-narrow-narrowing_error) +- [`gsl::narrow`](#user-content-H-narrow-narrow) + +### `gsl::narrowing_error` + +`gsl::narrowing_error` is the exception thrown by [`gsl::narrow`](#user-content-H-narrow-narrow) when a narrowing conversion fails. It is derived from `std::exception`. + +### `gsl::narrow` + +`gsl::narrow(x)` is a named cast that does a `static_cast(x)` for narrowing conversions with no signedness promotions. +If the argument `x` cannot be represented in the target type `T`, then the function throws a [`gsl::narrowing_error`](#user-content-H-narrow-narrowing_error) (e.g., `narrow(-42)` and `narrow(300)` throw). + +Note: compare [`gsl::narrow_cast`](#user-content-H-util-narrow_cast) in header [util](#user-content-H-util). + +See [ES.46: Avoid lossy (narrowing, truncating) arithmetic conversions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-narrowing) and [ES.49: If you must use a cast, use a named cast](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-casts-named) + +## `` + +This header contains some pointer types. + +See [GSL.view](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-views) + +- [`gsl::unique_ptr`](#user-content-H-pointers-unique_ptr) +- [`gsl::shared_ptr`](#user-content-H-pointers-shared_ptr) +- [`gsl::owner`](#user-content-H-pointers-owner) +- [`gsl::not_null`](#user-content-H-pointers-not_null) +- [`gsl::strict_not_null`](#user-content-H-pointers-strict_not_null) + +### `gsl::unique_ptr` + +`gsl::unique_ptr` is an alias to `std::unique_ptr`. + +See [GSL.owner: Ownership pointers](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-ownership) + +### `gsl::shared_ptr` + +`gsl::shared_ptr` is an alias to `std::shared_ptr`. + +See [GSL.owner: Ownership pointers](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-ownership) + +### `gsl::owner` + +`gsl::owner` is designed as a safety mechanism for code that must deal directly with raw pointers that own memory. Ideally such code should be restricted to the implementation of low-level abstractions. `gsl::owner` can also be used as a stepping point in converting legacy code to use more modern RAII constructs such as smart pointers. +`T` must be a pointer type (`std::is_pointer`). + +A `gsl::owner` is a typedef to `T`. It adds no runtime overhead whatsoever, as it is purely syntactic and does not add any runtime checks. Instead, it serves as an annotation for static analysis tools which check for memory safety, and as a code comprehension guide for human readers. + +See Enforcement section of [C.31: All resources acquired by a class must be released by the class’s destructor](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-dtor-release). + +### `gsl::not_null` + +`gsl::not_null` restricts a pointer or smart pointer to only hold non-null values. It has no size overhead over `T`. + +The checks for ensuring that the pointer is not null are done in the constructor. There is no overhead when retrieving or dereferencing the checked pointer. +When a nullptr check fails, `std::terminate` is called. + +See [F.23: Use a `not_null` to indicate that “null” is not a valid value](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rf-nullptr) + +#### Member Types + +```cpp +using element_type = T; +``` + +The type of the pointer or smart pointer that is managed by this object. + +#### Member functions + +##### Construct/Copy + +```cpp +template ::value>> +constexpr not_null(U&& u); + +template ::value>> +constexpr not_null(T u); +``` + +Constructs a `gsl_owner` from a pointer that is convertible to `T` or that is a `T`. It [`Expects`](#user-content-H-assert-expects) that the provided pointer is not `== nullptr`. + +```cpp +template ::value>> +constexpr not_null(const not_null& other); +``` + +Constructs a `gsl_owner` from another `gsl_owner` where the other pointer is convertible to `T`. It [`Expects`](#user-content-H-assert-expects) that the provided pointer is not `== nullptr`. + +```cpp +not_null(const not_null& other) = default; +not_null& operator=(const not_null& other) = default; +``` + +Copy construction and assignment. + +```cpp +not_null(std::nullptr_t) = delete; +not_null& operator=(std::nullptr_t) = delete; +``` + +Construction from `std::nullptr_t` and assignment of `std::nullptr_t` are explicitly deleted. + +##### Modifiers + +```cpp +not_null& operator++() = delete; +not_null& operator--() = delete; +not_null operator++(int) = delete; +not_null operator--(int) = delete; +not_null& operator+=(std::ptrdiff_t) = delete; +not_null& operator-=(std::ptrdiff_t) = delete; +``` + +Explicitly deleted operators. Pointers point to single objects ([I.13: Do not pass an array as a single pointer](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ri-array)), so don't allow these operators. + +##### Observers + +```cpp +constexpr details::value_or_reference_return_t get() const; +constexpr operator T() const { return get(); } +``` + +Get the underlying pointer. + +```cpp +constexpr decltype(auto) operator->() const { return get(); } +constexpr decltype(auto) operator*() const { return *get(); } +``` + +Dereference the underlying pointer. + +```cpp +void operator[](std::ptrdiff_t) const = delete; +``` + +Array index operator is explicitly deleted. Pointers point to single objects ([I.13: Do not pass an array as a single pointer](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ri-array)), so don't allow treating them as an array. + +```cpp +void swap(not_null& other) { std::swap(ptr_, other.ptr_); } +``` + +Swaps contents with another `gsl::not_null` object. + +#### Non-member functions + +```cpp +template +auto make_not_null(T&& t) noexcept; +``` + +Creates a `gsl::not_null` object, deducing the target type from the type of the argument. + +```cpp +template ::value && std::is_move_constructible::value, bool> = true> +void swap(not_null& a, not_null& b); +``` + +Swaps the contents of two `gsl::not_null` objects. + +```cpp +template +auto operator==(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(lhs.get() == rhs.get())) + -> decltype(lhs.get() == rhs.get()); +template +auto operator!=(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(lhs.get() != rhs.get())) + -> decltype(lhs.get() != rhs.get()); +template +auto operator<(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(lhs.get() < rhs.get())) + -> decltype(lhs.get() < rhs.get()); +template +auto operator<=(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(lhs.get() <= rhs.get())) + -> decltype(lhs.get() <= rhs.get()); +template +auto operator>(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(lhs.get() > rhs.get())) + -> decltype(lhs.get() > rhs.get()); +template +auto operator>=(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(lhs.get() >= rhs.get())) + -> decltype(lhs.get() >= rhs.get()); +``` + +Comparison of pointers that are convertible to each other. + +##### Input/Output + +```cpp +template +std::ostream& operator<<(std::ostream& os, const not_null& val); +``` + +Performs stream output on a `not_null` pointer, invoking `os << val.get()`. This function is only available when `GSL_NO_IOSTREAMS` is not defined. + +##### Modifiers + +```cpp +template +std::ptrdiff_t operator-(const not_null&, const not_null&) = delete; +template +not_null operator-(const not_null&, std::ptrdiff_t) = delete; +template +not_null operator+(const not_null&, std::ptrdiff_t) = delete; +template +not_null operator+(std::ptrdiff_t, const not_null&) = delete; +``` + +Addition and subtraction are explicitly deleted. Pointers point to single objects ([I.13: Do not pass an array as a single pointer](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ri-array)), so don't allow these operators. + +##### STL integration + +```cpp +template +struct std::hash> { ... }; +``` + +Specialization of `std::hash` for `gsl::not_null`. + +### `gsl::strict_not_null` + +`strict_not_null` is the same as [`not_null`](#user-content-H-pointers-not_null) except that the constructors are `explicit`. + +The free function that deduces the target type from the type of the argument and creates a `gsl::strict_not_null` object is `gsl::make_strict_not_null`. + +## `` + +This header file exports the class `gsl::span`, a bounds-checked implementation of `std::span`. + +- [`gsl::span`](#user-content-H-span-span) + +### `gsl::span` + +```cpp +template +class span; +``` + +`gsl::span` is a view over memory. It does not own the memory and is only a way to access contiguous sequences of objects. +The extent can be either a fixed size or [`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent). + +The `gsl::span` is based on the standardized version of `std::span` which was added to C++20. Originally, the plan was to +deprecate `gsl::span` when `std::span` finished standardization, however that plan changed when the runtime bounds checking +was removed from `std::span`'s design. + +The key differences between `gsl::span` and `std::span` are: +- `gsl::span` strictly enforces runtime bounds checking for all access operations +- Any violations of the bounds check results in termination of the program +- `gsl::span`'s iterators also perform bounds checking, unlike `std::span`'s iterators + +#### Which version of span should I use? + +The following table compares the different span implementations to help you choose which one is best for your project: + +| Feature/Version | `std::span` (C++20/23) | Hardened `std::span` (C++26) | `gsl::span` | +|-----------------|------------------------|------------------------------|-------------| +| **C++ Standard** | Requires C++20 or later | Requires C++26 or backported implementation | Works with C++14 or later | +| **Element Access** | No bounds checking | Bounds checking | Bounds checking | +| **Iterator Safety** | No bounds checking | Implementation-defined, may depend on vendor | Full bounds checking | +| **Error Behavior** | Undefined behavior on invalid access | Implementation-defined, may be configurable | Always calls [`std::terminate()`](https://en.cppreference.com/w/cpp/error/terminate) via [gsl::details::terminate()](https://github.com/microsoft/GSL/blob/main/include/gsl/assert#L111-L118) | +| **Performance** | Fastest (no checking) | Varies by implementation and configuration | May have performance impact from bounds checking | + +**Recommendations:** + +- **C++14 & C++17 projects**: Use `gsl::span` as `std::span` is not available. +- **C++20 & C++23 projects**: + - Use `gsl::span` if safety is your priority. + - Use `std::span` if performance is critical and you're confident in your index calculations. +- **C++26 projects**: + - Use `gsl::span` if you need guaranteed iterator safety across all platforms. + - Use hardened `std::span` if you want standard library compliance and acceptable safety. + +**Implementation notes for hardened `std::span` in C++26:** +- For MSVC: See [Microsoft STL Hardening](https://github.com/microsoft/STL/wiki/STL-Hardening) +- For Clang/LLVM: See [libc++ Hardening](https://libcxx.llvm.org/Hardening.html) + +#### Types + +```cpp +using element_type = ElementType; +using value_type = std::remove_cv_t; +using size_type = std::size_t; +using pointer = element_type*; +using const_pointer = const element_type*; +using reference = element_type&; +using const_reference = const element_type&; +using difference_type = std::ptrdiff_t; + +using iterator = details::span_iterator; +using reverse_iterator = std::reverse_iterator; +``` + +#### Member functions + +```cpp +constexpr span() noexcept; +``` + +Constructs an empty `span`. This constructor is only available if `Extent` is 0 or [`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent). +`span::data()` will return `nullptr`. + +```cpp +constexpr explicit(Extent != gsl::dynamic_extent) span(pointer ptr, size_type count) noexcept; +``` + +Constructs a `span` from a pointer and a size. If `Extent` is not [`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent), +then the constructor [`Expects`](#user-content-H-assert-expects) that `count == Extent`. + +```cpp +constexpr explicit(Extent != gsl::dynamic_extent) span(pointer firstElem, pointer lastElem) noexcept; +``` + +Constructs a `span` from a pointer to the begin and the end of the data. If `Extent` is not [`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent), +then the constructor [`Expects`](#user-content-H-assert-expects) that `lastElem - firstElem == Extent`. + +```cpp +template +constexpr span(element_type (&arr)[N]) noexcept; +``` + +Constructs a `span` from a C style array. This overload is available if `Extent ==`[`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent) +or `N == Extent`. + +```cpp +template +constexpr span(std::array& arr) noexcept; + +template +constexpr span(const std::array& arr) noexcept; +``` + +Constructs a `span` from a `std::array`. These overloads are available if `Extent ==`[`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent) +or `N == Extent`, and if the array can be interpreted as a `ElementType` array. + +```cpp +template +constexpr explicit(Extent != gsl::dynamic_extent) span(Container& cont) noexcept; + +template +constexpr explicit(Extent != gsl::dynamic_extent) span(const Container& cont) noexcept; +``` + +Constructs a `span` from a container. These overloads are available if `Extent ==`[`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent) +or `N == Extent`, and if the container can be interpreted as a contiguous `ElementType` array. + +```cpp +constexpr span(const span& other) noexcept = default; +``` + +Copy constructor. + +```cpp +template +explicit(Extent != gsl::dynamic_extent && OtherExtent == dynamic_extent) +constexpr span(const span& other) noexcept; +``` + +Constructs a `span` from another `span`. This constructor is available if `OtherExtent == Extent || Extent ==`[`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent)` || OtherExtent ==`[`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent) +and if `ElementType` and `OtherElementType` are compatible. + +If `Extent !=`[`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent) and `OtherExtent ==`[`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent), +then the constructor [`Expects`](#user-content-H-assert-expects) that `other.size() == Extent`. + +```cpp +constexpr span& operator=(const span& other) noexcept = default; +``` + +Copy assignment + +```cpp +template +constexpr span first() const noexcept; + +constexpr span first(size_type count) const noexcept; + +template +constexpr span last() const noexcept; + +constexpr span last(size_type count) const noexcept; +``` + +Return a subspan of the first/last `Count` elements. [`Expects`](#user-content-H-assert-expects) that `Count` does not exceed the `span`'s size. + +```cpp +template +constexpr auto subspan() const noexcept; + +constexpr span +subspan(size_type offset, size_type count = dynamic_extent) const noexcept; +``` + +Return a subspan starting at `offset` and having size `count`. [`Expects`](#user-content-H-assert-expects) that `offset` does not exceed the `span`'s size, +and that `offset == `[`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent) or `offset + count` does not exceed the `span`'s size. +If `count` is `gsl::dynamic_extent`, the number of elements in the subspan is `size() - offset`. + +```cpp +constexpr size_type size() const noexcept; + +constexpr size_type size_bytes() const noexcept; +``` + +Returns the size respective the size in bytes of the `span`. + +```cpp +constexpr bool empty() const noexcept; +``` + +Is the `span` empty? + +```cpp +constexpr reference operator[](size_type idx) const noexcept; +``` + +Returns a reference to the element at the given index. [`Expects`](#user-content-H-assert-expects) that `idx` is less than the `span`'s size. + +```cpp +constexpr reference front() const noexcept; +constexpr reference back() const noexcept; +``` + +Returns a reference to the first/last element in the `span`. [`Expects`](#user-content-H-assert-expects) that the `span` is not empty. + +```cpp +constexpr pointer data() const noexcept; +``` + +Returns a pointer to the beginning of the contained data. + +```cpp +constexpr iterator begin() const noexcept; +constexpr iterator end() const noexcept; +constexpr reverse_iterator rbegin() const noexcept; +constexpr reverse_iterator rend() const noexcept; +``` + +Returns an iterator to the first/last normal/reverse iterator. + +```cpp +template +span(Type (&)[Extent]) -> span; + +template +span(std::array&) -> span; + +template +span(const std::array&) -> span; + +template ().data())>> +span(Container&) -> span; + +template ().data())>> +span(const Container&) -> span; +``` + +Deduction guides. + +```cpp +template +span::value> +as_bytes(span s) noexcept; + +template +span::value> +as_writable_bytes(span s) noexcept; +``` + +Converts a `span` into a `span` of `byte`s. + +`as_writable_bytes` will only be available for non-const `ElementType`s. + +## `` + +This file is a companion for and included by [``](#user-content-H-span), and should not be used on its own. It contains useful features that aren't part of the `std::span` API as found inside the STL `` header (with the exception of [`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent), which is included here due to implementation constraints). + +- [`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent) +- [`gsl::span`](#user-content-H-span_ext-span) +- [`gsl::span` comparison operators](#user-content-H-span_ext-span_comparison_operators) +- [`gsl::make_span`](#user-content-H-span_ext-make_span) +- [`gsl::at`](#user-content-H-span_ext-at) +- [`gsl::ssize`](#user-content-H-span_ext-ssize) +- [`gsl::span` iterator functions](#user-content-H-span_ext-span_iterator_functions) + +### `gsl::dynamic_extent` + +Defines the extent value to be used by all `gsl::span` with dynamic extent. + +Note: `std::dynamic_extent` is exposed by the STL `` header and so ideally `gsl::dynamic_extent` would be under [``](#user-content-H-span), but to avoid cyclic dependency issues it is under `` instead. + +### `gsl::span` + +```cpp +template +class span; +``` + +Forward declaration of `gsl::span`. + +### `gsl::span` comparison operators + +```cpp +template +constexpr bool operator==(span l, span r); +template +constexpr bool operator!=(span l, span r); +template +constexpr bool operator<(span l, span r); +template +constexpr bool operator<=(span l, span r); +template +constexpr bool operator>(span l, span r); +template +constexpr bool operator>=(span l, span r); +``` + +The comparison operators for two `span`s lexicographically compare the elements in the `span`s. + +### `gsl::make_span` + +```cpp +template +constexpr span make_span(ElementType* ptr, typename span::size_type count); +template +constexpr span make_span(ElementType* firstElem, ElementType* lastElem); +template +constexpr span make_span(ElementType (&arr)[N]) noexcept; +template +constexpr span make_span(Container& cont); +template +constexpr span make_span(const Container& cont); +``` + +Utility function for creating a `span` with [`gsl::dynamic_extent`](#user-content-H-span_ext-dynamic_extent) from +- pointer and length, +- pointer to start and pointer to end, +- a C style array, or +- a container. + +### `gsl::at` + +```cpp +template +constexpr ElementType& at(span s, index i); +``` + +The function `gsl::at` offers a safe way to access data with index bounds checking. + +This is the specialization of [`gsl::at`](#user-content-H-util-at) for [`span`](#user-content-H-span-span). It returns a reference to the `i`th element and +[`Expects`](#user-content-H-assert-expects) that the provided index is within the bounds of the `span`. + +Note: `gsl::at` supports indexes up to `PTRDIFF_MAX`. + +### `gsl::ssize` + +```cpp +template +constexpr std::ptrdiff_t ssize(const span& s) noexcept; +``` + +Return the size of a [`span`](#user-content-H-span-span) as a `ptrdiff_t`. + +### `gsl::span` iterator functions + +```cpp +template +constexpr typename span::iterator +begin(const span& s) noexcept; + +template +constexpr typename span::iterator +end(const span& s) noexcept; + +template +constexpr typename span::reverse_iterator +rbegin(const span& s) noexcept; + +template +constexpr typename span::reverse_iterator +rend(const span& s) noexcept; + +template +constexpr typename span::iterator +cbegin(const span& s) noexcept; + +template +constexpr typename span::iterator +cend(const span& s) noexcept; + +template +constexpr typename span::reverse_iterator +crbegin(const span& s) noexcept; + +template +constexpr typename span::reverse_iterator +crend(const span& s) noexcept; +``` + +Free functions for getting a non-const/const begin/end normal/reverse iterator for a [`span`](#user-content-H-span-span). + +## `` + +This header exports a family of `*zstring` types. + +A `gsl::XXzstring` is a typedef to `T`. It adds no checks whatsoever, it is just for having a syntax to describe +that a pointer points to a zero terminated C style string. This helps static code analysis, and it helps human readers. + +`basic_zstring` is a pointer to a C-string (zero-terminated array) with a templated char type. Used to implement the rest of the `*zstring` family. +`zstring` is a zero terminated `char` string. +`czstring` is a const zero terminated `char` string. +`wzstring` is a zero terminated `wchar_t` string. +`cwzstring` is a const zero terminated `wchar_t` string. +`u16zstring` is a zero terminated `char16_t` string. +`cu16zstring` is a const zero terminated `char16_t` string. +`u32zstring` is a zero terminated `char32_t` string. +`cu32zstring` is a const zero terminated `char32_t` string. + +See [GSL.view](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-views) and [SL.str.3: Use zstring or czstring to refer to a C-style, zero-terminated, sequence of characters](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rstr-zstring). + +## `` + +This header contains utility functions and classes. This header works without exceptions being available. The parts that require +exceptions being available are in their own header file [narrow](#user-content-H-narrow). + +See [GSL.util: Utilities](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ss-utilities) + +- [`gsl::narrow_cast`](#user-content-H-util-narrow_cast) +- [`gsl::final_action`](#user-content-H-util-final_action) +- [`gsl::at`](#user-content-H-util-at) + +### `gsl::index` + +An alias to `std::ptrdiff_t`. It serves as the index type for all container indexes/subscripts/sizes. + +### `gsl::narrow_cast` + +`gsl::narrow_cast(x)` is a named cast that is identical to a `static_cast(x)`. It exists to make clear to static code analysis tools and to human readers that a lossy conversion is acceptable. + +Note: compare the throwing version [`gsl::narrow`](#user-content-H-narrow-narrow) in header [narrow](#user-content-H-narrow). + +See [ES.46: Avoid lossy (narrowing, truncating) arithmetic conversions](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-narrowing) and [ES.49: If you must use a cast, use a named cast](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-casts-named) + +### `gsl::final_action` + +```cpp +template +class final_action { ... }; +``` + +`final_action` allows you to ensure something gets run at the end of a scope. + +See [E.19: Use a final_action object to express cleanup if no suitable resource handle is available](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#re-finally) + +#### Member functions + +```cpp +explicit final_action(const F& ff) noexcept; +explicit final_action(F&& ff) noexcept; +``` + +Construct an object with the action to invoke in the destructor. + +```cpp +~final_action() noexcept; +``` + +The destructor will call the action that was passed in the constructor. + +```cpp +final_action(final_action&& other) noexcept; +final_action(const final_action&) = delete; +void operator=(const final_action&) = delete; +void operator=(final_action&&) = delete; +``` + +Move construction is allowed. Copy construction is deleted. Copy and move assignment are also explicitly deleted. + +#### Non-member functions +```cpp +template +auto finally(F&& f) noexcept; +``` + +Creates a `gsl::final_action` object, deducing the template argument type from the type of the argument. + +### `gsl::at` + +The function `gsl::at` offers a safe way to access data with index bounds checking. + +Note: `gsl::at` supports indexes up to `PTRDIFF_MAX`. + +See [ES.42: Keep use of pointers simple and straightforward](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-ptr) + +```cpp +template +constexpr T& at(T (&arr)[N], const index i); +``` + +This overload returns a reference to the `i`s element of a C style array `arr`. It [`Expects`](#user-content-H-assert-expects) that the provided index is within the bounds of the array. + +```cpp +template +constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]); +``` + +This overload returns a reference to the `i`s element of the container `cont`. It [`Expects`](#user-content-H-assert-expects) that the provided index is within the bounds of the array. + +```cpp +template +constexpr T at(const std::initializer_list cont, const index i); +``` + +This overload returns a reference to the `i`s element of the initializer list `cont`. It [`Expects`](#user-content-H-assert-expects) that the provided index is within the bounds of the array. + +```cpp +template +constexpr auto at(std::span sp, const index i) -> decltype(sp[sp.size()]); +``` + +This overload returns a reference to the `i`s element of the `std::span` `sp`. It [`Expects`](#user-content-H-assert-expects) that the provided index is within the bounds of the array. + +For [`gsl::at`](#user-content-H-span_ext-at) for [`gsl::span`](#user-content-H-span-span) see header [`span_ext`](#user-content-H-span_ext). + +```cpp +template ::value && std::is_move_constructible::value>> +void swap(T& a, T& b); +``` + +Swaps the contents of two objects. Exists only to specialize `gsl::swap(gsl::not_null&, gsl::not_null&)`. diff --git a/kernel/third_party/GSL-5.0.0/docs/upgrade_checklist.md b/kernel/third_party/GSL-5.0.0/docs/upgrade_checklist.md new file mode 100644 index 0000000..7c0c765 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/docs/upgrade_checklist.md @@ -0,0 +1,17 @@ +> When bumping the version, you need to update the following files: + +1. [ ] [CMakeLists.txt](../CMakeLists.txt) Bump `GSL_VERSION` +1. [ ] [README.md](../README.md) Bump `GIT_TAG` + +> After updating, you need to create a new GitHub release: + +1. [ ] [Microsoft/GSL - Create Release](https://github.com/microsoft/GSL/releases/new) + +Be sure to update the release notes accordingly and properly mention open-source +contributors. + +> After a new release exists, update the `ms-gsl` vcpkg port: + +1. [ ] [Microsoft/vcpkg - ms-gsl port](https://github.com/microsoft/vcpkg/tree/master/ports/ms-gsl) + +Be sure to monitor the PR that updates the port for any feedback from vcpkg maintainers. diff --git a/kernel/third_party/GSL-5.0.0/include/CMakeLists.txt b/kernel/third_party/GSL-5.0.0/include/CMakeLists.txt new file mode 100644 index 0000000..fe4eed9 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/CMakeLists.txt @@ -0,0 +1,13 @@ +# Add include folders to the library and targets that consume it +# the SYSTEM keyword suppresses warnings for users of the library +# +# By adding this directory as an include directory the user gets a +# namespace effect. +# +# IE: +# #include +if(PROJECT_IS_TOP_LEVEL) + target_include_directories(GSL INTERFACE $) +else() + target_include_directories(GSL SYSTEM INTERFACE $) +endif() diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/algorithm b/kernel/third_party/GSL-5.0.0/include/gsl/algorithm new file mode 100644 index 0000000..2098906 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/algorithm @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_ALGORITHM_H +#define GSL_ALGORITHM_H + +#include "./assert" // for Expects +#include "./span" // for dynamic_extent, span + +#include // for copy_n +#include // for ptrdiff_t +#include // for is_assignable + +#ifdef _MSC_VER +#pragma warning(push) + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4996) // unsafe use of std::copy_n + +#endif // _MSC_VER + +namespace gsl +{ +// Note: this will generate faster code than std::copy using span iterator in older msvc+stl +// not necessary for msvc since VS2017 15.8 (_MSC_VER >= 1915) +template +void copy(span src, span dest) +{ + static_assert(std::is_assignable::value, + "Elements of source span can not be assigned to elements of destination span"); + static_assert(SrcExtent == dynamic_extent || DestExtent == dynamic_extent || + (SrcExtent <= DestExtent), + "Source range is longer than target range"); + + Expects(dest.size() >= src.size()); + GSL_SUPPRESS(stl.1) + std::copy_n(src.data(), src.size(), dest.data()); +} + +} // namespace gsl + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // GSL_ALGORITHM_H diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/assert b/kernel/third_party/GSL-5.0.0/include/gsl/assert new file mode 100644 index 0000000..daa1a75 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/assert @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_ASSERT_H +#define GSL_ASSERT_H + +// +// Temporary until MSVC STL supports no-exceptions mode. +// Currently terminate is a no-op in this mode, so we add termination behavior back +// +#if defined(_MSC_VER) && (defined(_KERNEL_MODE) || (defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS)) +#define GSL_KERNEL_MODE + +#define GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND +#include +#define RANGE_CHECKS_FAILURE 0 + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winvalid-noreturn" +#if __clang_major__ >= 22 +#pragma clang diagnostic ignored "-Wunique-object-duplication" +#endif // __clang_major >= 22 +#endif // defined(__clang__) + +#else // defined(_MSC_VER) && (defined(_KERNEL_MODE) || (defined(_HAS_EXCEPTIONS) && + // !_HAS_EXCEPTIONS)) + +#include + +#endif // defined(_MSC_VER) && (defined(_KERNEL_MODE) || (defined(_HAS_EXCEPTIONS) && + // !_HAS_EXCEPTIONS)) + +// +// make suppress attributes parse for some compilers +// Hopefully temporary until suppression standardization occurs +// +#if defined(__clang__) +#define GSL_SUPPRESS(x) [[gsl::suppress(#x)]] +#elif defined(_MSC_VER) && _MSC_VER >= 1950 +// Visual Studio versions after 2022 (_MSC_VER > 1944) support the justification message. +#define GSL_SUPPRESS(x) [[gsl::suppress(#x)]] +#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__NVCC__) +#define GSL_SUPPRESS(x) [[gsl::suppress(x)]] +#else +#define GSL_SUPPRESS(x) +#endif // defined(__clang__) + +#if defined(__clang__) || defined(__GNUC__) +#define GSL_LIKELY(x) __builtin_expect(!!(x), 1) +#define GSL_UNLIKELY(x) __builtin_expect(!!(x), 0) + +#else + +#define GSL_LIKELY(x) (!!(x)) +#define GSL_UNLIKELY(x) (!!(x)) +#endif // defined(__clang__) || defined(__GNUC__) + +// +// GSL_ASSUME(cond) +// +// Tell the optimizer that the predicate cond must hold. It is unspecified +// whether or not cond is actually evaluated. +// +#ifdef _MSC_VER +#define GSL_ASSUME(cond) __assume(cond) +#elif defined(__GNUC__) +#define GSL_ASSUME(cond) ((cond) ? static_cast(0) : __builtin_unreachable()) +#else +#define GSL_ASSUME(cond) static_cast((cond) ? 0 : 0) +#endif + +// +// GSL.assert: assertions +// + +namespace gsl +{ + +namespace details +{ +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + + typedef void(__cdecl* terminate_handler)(); + + GSL_SUPPRESS(f.6) + [[noreturn]] inline void __cdecl default_terminate_handler() + { + __fastfail(RANGE_CHECKS_FAILURE); + } + + inline gsl::details::terminate_handler& get_terminate_handler() noexcept + { + static terminate_handler handler = &default_terminate_handler; + return handler; + } + +#endif // defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + + [[noreturn]] inline void terminate() noexcept + { +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + (*gsl::details::get_terminate_handler())(); +#else + std::terminate(); +#endif // defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + } + +} // namespace details +} // namespace gsl + +#define GSL_CONTRACT_CHECK(type, cond) \ + (GSL_LIKELY(cond) ? static_cast(0) : gsl::details::terminate()) + +#define Expects(cond) GSL_CONTRACT_CHECK("Precondition", cond) +#define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond) + +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) && defined(__clang__) +#pragma clang diagnostic pop +#endif + +#endif // GSL_ASSERT_H diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/byte b/kernel/third_party/GSL-5.0.0/include/gsl/byte new file mode 100644 index 0000000..b532b5f --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/byte @@ -0,0 +1,201 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_BYTE_H +#define GSL_BYTE_H + +#include "./util" // for GSL_DEPRECATED + +#include + +#ifdef _MSC_VER + +#pragma warning(push) + +// Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool. +#pragma warning(disable : 26493) // don't use c-style casts // TODO: MSVC suppression in templates + // does not always work + +#ifndef GSL_USE_STD_BYTE +// this tests if we are under MSVC and the standard lib has std::byte and it is enabled +#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603 + +#define GSL_USE_STD_BYTE 1 + +#else // defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603 + +#define GSL_USE_STD_BYTE 0 + +#endif // defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603 +#endif // GSL_USE_STD_BYTE + +#else // _MSC_VER + +#ifndef GSL_USE_STD_BYTE +#include /* __cpp_lib_byte */ +// this tests if we are under GCC or Clang with enough -std=c++1z power to get us std::byte +// also check if libc++ version is sufficient (> 5.0) or libstdc++ actually contains std::byte +#if defined(__cplusplus) && (__cplusplus >= 201703L) && \ + (defined(__cpp_lib_byte) && (__cpp_lib_byte >= 201603) || \ + defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 5000)) + +#define GSL_USE_STD_BYTE 1 + +#else // defined(__cplusplus) && (__cplusplus >= 201703L) && + // (defined(__cpp_lib_byte) && (__cpp_lib_byte >= 201603) || + // defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 5000)) + +#define GSL_USE_STD_BYTE 0 + +#endif // defined(__cplusplus) && (__cplusplus >= 201703L) && + // (defined(__cpp_lib_byte) && (__cpp_lib_byte >= 201603) || + // defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 5000)) +#endif // GSL_USE_STD_BYTE + +#endif // _MSC_VER + +// Use __may_alias__ attribute on gcc and clang +#if defined __clang__ || (defined(__GNUC__) && __GNUC__ > 5) +#define byte_may_alias __attribute__((__may_alias__)) +#else // defined __clang__ || defined __GNUC__ +#define byte_may_alias +#endif // defined __clang__ || defined __GNUC__ + +#if GSL_USE_STD_BYTE +#include +#endif + +namespace gsl +{ +#if GSL_USE_STD_BYTE + +namespace impl +{ + // impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as + // would be the case when we used gsl::byte. Users of GSL should only use gsl::byte, not + // gsl::impl::byte. + using byte = std::byte; +} // namespace impl + +using byte GSL_DEPRECATED("Use std::byte instead.") = std::byte; + +using std::to_integer; + +#else // GSL_USE_STD_BYTE + +// This is a simple definition for now that allows +// use of byte within span<> to be standards-compliant +enum class byte_may_alias byte : unsigned char +{ +}; + +namespace impl +{ + // impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as + // would be the case when we used gsl::byte. Users of GSL should only use gsl::byte, not + // gsl::impl::byte. + using byte = gsl::byte; +} // namespace impl + +template ::value, bool> = true> +constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) << shift); +} + +template ::value, bool> = true> +constexpr byte operator<<(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) << shift); +} + +template ::value, bool> = true> +constexpr byte& operator>>=(byte& b, IntegerType shift) noexcept +{ + return b = byte(static_cast(b) >> shift); +} + +template ::value, bool> = true> +constexpr byte operator>>(byte b, IntegerType shift) noexcept +{ + return byte(static_cast(b) >> shift); +} + +constexpr byte& operator|=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) | static_cast(r)); +} + +constexpr byte operator|(byte l, byte r) noexcept +{ + return byte(static_cast(l) | static_cast(r)); +} + +constexpr byte& operator&=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) & static_cast(r)); +} + +constexpr byte operator&(byte l, byte r) noexcept +{ + return byte(static_cast(l) & static_cast(r)); +} + +constexpr byte& operator^=(byte& l, byte r) noexcept +{ + return l = byte(static_cast(l) ^ static_cast(r)); +} + +constexpr byte operator^(byte l, byte r) noexcept +{ + return byte(static_cast(l) ^ static_cast(r)); +} + +constexpr byte operator~(byte b) noexcept { return byte(~static_cast(b)); } + +template ::value, bool> = true> +constexpr IntegerType to_integer(byte b) noexcept +{ + return static_cast(b); +} + +#endif // GSL_USE_STD_BYTE + +template +constexpr gsl::impl::byte to_byte(T t) noexcept +{ + static_assert( + std::is_same::value, + "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. " + "If you are calling to_byte with an integer constant use: gsl::to_byte() version."); + return gsl::impl::byte(t); +} + +template +constexpr gsl::impl::byte to_byte() noexcept +{ + static_assert(I >= 0 && I <= 255, + "gsl::byte only has 8 bits of storage, values must be in range 0-255"); + return static_cast(I); +} + +} // namespace gsl + +#ifdef _MSC_VER +#pragma warning(pop) +#endif // _MSC_VER + +#endif // GSL_BYTE_H diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/dyn_array b/kernel/third_party/GSL-5.0.0/include/gsl/dyn_array new file mode 100644 index 0000000..a54bd1d --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/dyn_array @@ -0,0 +1,446 @@ +// -*- C++ -*- +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2026 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_DYN_ARRAY_H +#define GSL_DYN_ARRAY_H + +#include "./assert" +#include "./narrow" +#include "./util" + +#include +#include +#include +#include +#include + +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) +#include +#endif /* __cpp_lib_ranges >= 201911L */ + +namespace gsl +{ +template > +class dyn_array; + +namespace details +{ + template > + class dyn_array_base : public Allocator + { + using pointer = T*; + using size_type = std::size_t; + + template + GSL_CONSTEXPR_SINCE_CPP20 void construct(pointer ptr, Args&&... args) + { + std::allocator_traits::construct(static_cast(*this), ptr, + std::forward(args)...); + } + + GSL_CONSTEXPR_SINCE_CPP20 void destroy(pointer ptr) + { + std::allocator_traits::destroy(static_cast(*this), ptr); + } + + GSL_CONSTEXPR_SINCE_CPP20 void destroy_range(pointer first, pointer last) + { + for (; first != last; ++first) { destroy(first); } + } + + GSL_CONSTEXPR_SINCE_CPP20 void rollback_construction(pointer first, pointer last) + { + destroy_range(first, last); + std::allocator_traits::deallocate(static_cast(*this), _data, + _count); + _data = nullptr; + _count = 0; + } + + protected: + constexpr auto data() const { return _data; } + + constexpr auto count() const { return _count; } + + GSL_CONSTEXPR_SINCE_CPP20 void resize(size_type count) + { + // This should only be called when constructing a non-forward iterator. + // It neither frees nor copies `_data`. + Expects(_data == nullptr && _count == 0); + if (count != 0) + { + _data = std::allocator_traits::allocate(static_cast(*this), + count); + _count = count; + } + } + + GSL_CONSTEXPR_SINCE_CPP20 void fill(pointer first, size_type count, const T& value) + { + pointer current = first; + try + { + for (size_type i = 0; i < count; ++i, ++current) { construct(current, value); } + } catch (...) + { + rollback_construction(first, current); + throw; + } + } + + template + GSL_CONSTEXPR_SINCE_CPP20 void copy(InputIt first, InputIt last, pointer output) + { + pointer current = output; + try + { + for (; first != last; ++first, ++current) { construct(current, *first); } + } catch (...) + { + rollback_construction(output, current); + throw; + } + } + + GSL_CONSTEXPR_SINCE_CPP20 void default_construct(pointer first, size_type count) + { + pointer current = first; + try + { + for (size_type i = 0; i < count; ++i, ++current) { construct(current); } + } catch (...) + { + rollback_construction(first, current); + throw; + } + } + + private: + pointer _data; + size_type _count; + + public: + constexpr dyn_array_base(const Allocator& alloc) + : Allocator{alloc}, _data{nullptr}, _count{0} + { + Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr)); + } + + constexpr dyn_array_base(size_type count, const Allocator& alloc) + : Allocator{alloc} + , _data{count == 0 ? nullptr + : std::allocator_traits::allocate( + static_cast(*this), count)} + , _count{count} + { + Ensures((_count == 0 && _data == nullptr) || (_count > 0 && _data != nullptr)); + } + + GSL_CONSTEXPR_SINCE_CPP20 ~dyn_array_base() + { + if (_data) + { + if (!std::is_trivially_destructible::value) + { + destroy_range(_data, _data + _count); + } + std::allocator_traits::deallocate(static_cast(*this), _data, + _count); + } + } + }; + + template + class dyn_array_iterator + { + using size_type = std::size_t; + + public: + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using reference = T&; + using const_reference = const T&; + using iterator_category = std::random_access_iterator_tag; + +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) + constexpr dyn_array_iterator() = default; +#endif /* __cpp_lib_ranges >= 201911L */ + + constexpr operator dyn_array_iterator() const { return {_ptr, _pos, _end_pos}; } + +#if defined(_MSC_VER) && defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) + constexpr operator pointer() const { return _ptr + gsl::narrow(_pos); } +#endif /* defined(_MSC_VER) && __cpp_lib_ranges >= 201911L */ + + constexpr auto operator==(const dyn_array_iterator& other) const + { + Expects(_ptr == other._ptr); + Expects(_end_pos == other._end_pos); + return _pos == other._pos; + } + + constexpr auto operator!=(const dyn_array_iterator& other) const + { + return !(*this == other); + } + + constexpr auto operator*() const -> reference + { + Expects(_ptr != nullptr); + Expects(_pos < _end_pos); + return _ptr[_pos]; + } + + constexpr auto operator++() -> dyn_array_iterator& + { + Expects(_pos < _end_pos); + ++_pos; + return *this; + } + + constexpr auto operator++(int) + { + ++(*this); + return dyn_array_iterator{_ptr, _pos - 1, _end_pos}; + } + + constexpr auto operator--() -> dyn_array_iterator& + { + Expects(_pos > 0); + --_pos; + return *this; + } + + constexpr auto operator--(int) + { + --(*this); + return dyn_array_iterator{_ptr, _pos + 1, _end_pos}; + } + + constexpr auto operator+=(difference_type diff) -> dyn_array_iterator& + { + auto new_pos = gsl::narrow(_pos) + diff; + Expects(new_pos >= 0); + Expects(new_pos <= gsl::narrow(_end_pos)); + _pos = gsl::narrow(new_pos); + return *this; + } + + constexpr auto operator-=(difference_type diff) -> dyn_array_iterator& + { + auto new_pos = gsl::narrow(_pos) - diff; + Expects(new_pos >= 0); + Expects(new_pos <= gsl::narrow(_end_pos)); + _pos = gsl::narrow(new_pos); + return *this; + } + + constexpr auto operator+(difference_type diff) const + { + auto new_pos = gsl::narrow(_pos) + diff; + return dyn_array_iterator{_ptr, gsl::narrow(new_pos), _end_pos}; + } + + constexpr auto operator-(difference_type diff) const { return *this + (-diff); } + + constexpr auto operator-(const dyn_array_iterator& other) const + { + Expects(_ptr == other._ptr); + Expects(_end_pos == other._end_pos); + return gsl::narrow(_pos) - gsl::narrow(other._pos); + } + + constexpr auto operator[](size_type pos) -> reference + { + Expects(_pos + pos < _end_pos); + return _ptr[_pos + pos]; + } + + constexpr auto operator[](size_type pos) const -> const_reference + { + return const_cast(*this).operator[](pos); + } + + private: + constexpr dyn_array_iterator(pointer ptr, size_type pos, size_type end_pos) + : _ptr{ptr}, _pos{pos}, _end_pos{end_pos} + { + Ensures((_ptr != nullptr && _end_pos > 0) || (_ptr == nullptr && _end_pos == 0)); + Ensures(_pos <= _end_pos); + } + + pointer _ptr{}; + size_type _pos{}; + size_type _end_pos{}; + + template + friend class ::gsl::dyn_array; + }; +} // namespace details + +template +class dyn_array : private details::dyn_array_base +{ + using base = details::dyn_array_base; + using pointer = T*; + +public: + using value_type = T; + using reference = T&; + using const_reference = const T&; + using iterator = details::dyn_array_iterator; + using const_iterator = details::dyn_array_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using difference_type = std::ptrdiff_t; + using size_type = std::size_t; + + using allocator_type = Allocator; + + explicit constexpr dyn_array(const Allocator& alloc = {}) : base{alloc} {} + + constexpr dyn_array(size_type count, const T& value, const Allocator& alloc = {}) + : base{count, alloc} + { + base::fill(data(), size(), value); + } + + template ::value, bool> = true> + constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {}) + : base{gsl::narrow(std::distance(first, last)), alloc} + { + base::copy(first, last, data()); + } + + template ::value && + details::is_iterator::value, + bool> = true> + constexpr dyn_array(InputIt first, InputIt last, const Allocator& alloc = {}) : dyn_array{alloc} + { + std::vector tmp(first, last); + base::resize(tmp.size()); + base::copy(std::begin(tmp), std::end(tmp), data()); + } + +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) + template + requires(std::ranges::input_range) + constexpr dyn_array(std::from_range_t, InputRg&& rg, const Allocator& alloc = {}) + : base{gsl::narrow(std::size(rg)), alloc} + { + base::copy(std::ranges::begin(rg), std::ranges::end(rg), data()); + } +#endif /* __cpp_lib_containers_ranges >= 202202L */ + + constexpr explicit dyn_array(size_type count, const Allocator& alloc = {}) : base{count, alloc} + { + base::default_construct(data(), size()); + } + + constexpr dyn_array(const dyn_array& other, const Allocator& alloc = {}) + : dyn_array(other.begin(), other.end(), alloc) + {} + + constexpr dyn_array(std::initializer_list init, const Allocator& alloc = {}) + : dyn_array(init.begin(), init.end(), alloc) + {} + + constexpr dyn_array(dyn_array&&) = delete; + dyn_array& operator=(dyn_array&&) = delete; + + constexpr auto operator==(const dyn_array& other) const + { + return size() == other.size() && std::equal(begin(), end(), other.begin(), other.end()); + } + + constexpr auto operator!=(const dyn_array& other) const { return !(*this == other); } + + constexpr auto size() const { return base::count(); } + + constexpr auto empty() const { return size() == 0; } + + constexpr auto max_size() const { return static_cast(-1); } + + constexpr auto get_allocator() -> Allocator& { return *this; } + + constexpr auto operator[](size_type pos) -> reference + { + Expects(pos < size()); + return data()[pos]; + } + + constexpr auto operator[](size_type pos) const -> const_reference + { + return const_cast(*this)[pos]; + } + + constexpr auto data() { return base::data(); } + constexpr auto data() const -> const T* { return const_cast(*this).data(); } + + constexpr auto begin() { return iterator{data(), 0, size()}; } + constexpr auto begin() const { return const_iterator{data(), 0, size()}; } + constexpr auto cbegin() const { return begin(); } + + constexpr auto rbegin() { return reverse_iterator{end()}; } + constexpr auto rbegin() const { return const_reverse_iterator{end()}; } + constexpr auto crbegin() const { return rbegin(); } + +#ifdef _MSC_VER + constexpr auto _Unchecked_begin() { return data(); } + constexpr auto _Unchecked_begin() const -> const T* + { + return const_cast(*this)._Unchecked_begin(); + } +#endif /* _MSC_VER */ + + constexpr auto end() { return iterator{data(), size(), size()}; } + constexpr auto end() const { return const_iterator{data(), size(), size()}; } + constexpr auto cend() const { return end(); } + + constexpr auto rend() { return reverse_iterator{begin()}; } + constexpr auto rend() const { return const_reverse_iterator{begin()}; } + constexpr auto crend() const { return rend(); } + +#ifdef _MSC_VER + constexpr auto _Unchecked_end() { return data() + size(); } + constexpr auto _Unchecked_end() const -> const T* + { + return const_cast(*this)._Unchecked_end(); + } +#endif /* _MSC_VER */ +}; + +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) + +template ::value_type>> +dyn_array(InputIt, InputIt, Alloc = {}) + -> dyn_array::value_type, Alloc>; + +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) +template >> +dyn_array(std::from_range_t, InputRg&&, Alloc = {}) + -> dyn_array, Alloc>; +#endif /* __cpp_lib_containers_ranges >= 202202L */ + +#endif /* __cpp_deduction_guides >= 201703L */ +} // namespace gsl + +#endif /* defined(GSL_DYN_ARRAY_H) */ diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/gsl b/kernel/third_party/GSL-5.0.0/include/gsl/gsl new file mode 100644 index 0000000..3321faa --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/gsl @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_GSL_H +#define GSL_GSL_H + +// IWYU pragma: begin_exports +#include "./algorithm" // copy +#include "./assert" // Ensures/Expects +#include "./byte" // byte +#include "./dyn_array" // dyn_array +#include "./pointers" // owner, not_null +#include "./span" // span +#include "./util" // finally()/narrow_cast()... +#include "./zstring" // zstring + +#ifdef __cpp_exceptions +#include "./narrow" // narrow() +#endif +// IWYU pragma: end_exports + +#endif // GSL_GSL_H diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/narrow b/kernel/third_party/GSL-5.0.0/include/gsl/narrow new file mode 100644 index 0000000..f2f0114 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/narrow @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_NARROW_H +#define GSL_NARROW_H +#include "./assert" // for GSL_SUPPRESS +#include "./util" // for narrow_cast +#include // for std::exception +namespace gsl +{ +namespace details +{ + template + constexpr bool static_cast_is_defined(U u, /*is_floating_point_to_integral=*/std::true_type) + { + if (std::is_same::type, bool>::value) { return true; } + + U upper_bound{1}; + for (int i = 0; i < std::numeric_limits::digits; i++) + { + upper_bound *= std::numeric_limits::radix; + } + + if (u >= U{}) { return u < upper_bound; } + if (!std::is_signed::value) { return u > U{-1}; } + + return u + upper_bound > U{-1}; + } + + template + constexpr bool static_cast_is_defined(U, /*is_floating_point_to_integral=*/std::false_type) + { + return true; + } +} // namespace details + +struct narrowing_error : public std::exception +{ + const char* what() const noexcept override { return "narrowing_error"; } +}; + +// narrow() : a checked version of narrow_cast() that throws if the cast changed the value +template ::value>::type* = nullptr> +GSL_SUPPRESS(type.1) constexpr T narrow(U u) +{ + constexpr const bool is_different_signedness = + (std::is_signed::value != std::is_signed::value); + + using is_floating_point_to_integral = + std::integral_constant::value && std::is_floating_point::value>; + if (!details::static_cast_is_defined(u, is_floating_point_to_integral{})) + { + throw narrowing_error{}; + } + + GSL_SUPPRESS(es.103) // don't overflow + GSL_SUPPRESS(es.104) // don't underflow + const T t = narrow_cast(u); + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + // Note: NaN will always throw, since NaN != NaN + if (static_cast(t) != u || (is_different_signedness && ((t < T{}) != (u < U{})))) + { + throw narrowing_error{}; + } +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + + return t; +} + +template ::value>::type* = nullptr> +GSL_SUPPRESS(type.1) constexpr T narrow(U u) +{ + const T t = narrow_cast(u); + + if (static_cast(t) != u) { throw narrowing_error{}; } + + return t; +} +} // namespace gsl +#endif // GSL_NARROW_H diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/pointers b/kernel/third_party/GSL-5.0.0/include/gsl/pointers new file mode 100644 index 0000000..8aa471e --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/pointers @@ -0,0 +1,382 @@ +// -*- C++ -*- +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_POINTERS_H +#define GSL_POINTERS_H + +#include "./assert" // for Ensures, Expects +#include "./util" // for GSL_DEPRECATED + +#include // for ptrdiff_t, nullptr_t, size_t +#include // for less, greater +#include // for shared_ptr, unique_ptr, hash +#include // for enable_if_t, is_convertible, is_assignable +#include // for declval, forward + +#if !defined(GSL_NO_IOSTREAMS) +#include // for ostream +#endif // !defined(GSL_NO_IOSTREAMS) + +namespace gsl +{ + +namespace details +{ + template + struct is_comparable_to_nullptr : std::false_type + { + }; + + template + struct is_comparable_to_nullptr< + T, + std::enable_if_t() != nullptr), bool>::value>> + : std::true_type + { + }; + + // Resolves to the more efficient of `const T` or `const T&`, in the context of returning a + // const-qualified value of type T. + // + // Copied from cppfront's implementation of the CppCoreGuidelines F.16 + // (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rf-in) + template + using value_or_reference_return_t = + std::conditional_t::value, + const T, const T&>; + +} // namespace details + +// +// GSL.owner: ownership pointers +// +template +using shared_ptr GSL_DEPRECATED("Use std::shared_ptr instead") = std::shared_ptr; + +template +using unique_ptr GSL_DEPRECATED("Use std::unique_ptr instead") = std::unique_ptr; + +// +// owner +// +// `gsl::owner` is designed as a safety mechanism for code that must deal directly with raw +// pointers that own memory. Ideally such code should be restricted to the implementation of +// low-level abstractions. `gsl::owner` can also be used as a stepping point in converting legacy +// code to use more modern RAII constructs, such as smart pointers. +// +// T must be a pointer type +// - disallow construction from any type other than pointer type +// +template ::value, bool> = true> +using owner = T; + +// +// not_null +// +// Restricts a pointer or smart pointer to only hold non-null values. +// +// Has zero size overhead over T. +// +// If T is a pointer (i.e. T == U*) then +// - allow construction from U* +// - disallow construction from nullptr_t +// - disallow default construction +// - ensure construction from null U* fails +// - allow implicit conversion to U* +// +template +class not_null +{ +public: + static_assert(details::is_comparable_to_nullptr::value, "T cannot be compared to nullptr."); + + using element_type = T; + + template ::value>> + constexpr not_null(U&& u) noexcept(std::is_nothrow_move_constructible::value) + : ptr_(std::forward(u)) + { + Expects(ptr_ != nullptr); + } + + template ::value>> + constexpr not_null(T u) noexcept(std::is_nothrow_move_constructible::value) + : ptr_(std::move(u)) + { + Expects(ptr_ != nullptr); + } + + template ::value>> + constexpr not_null(const not_null& other) noexcept( + std::is_nothrow_move_constructible::value) + : not_null(other.get()) + {} + + not_null(const not_null& other) = default; + not_null& operator=(const not_null& other) = default; + constexpr details::value_or_reference_return_t get() const + noexcept(noexcept(details::value_or_reference_return_t(std::declval()))) + { + return ptr_; + } + + constexpr operator T() const { return get(); } + constexpr decltype(auto) operator->() const { return get(); } + constexpr decltype(auto) operator*() const { return *get(); } + + // prevents compilation when someone attempts to assign a null pointer constant + not_null(std::nullptr_t) = delete; + not_null& operator=(std::nullptr_t) = delete; + + // unwanted operators...pointers only point to single objects! + not_null& operator++() = delete; + not_null& operator--() = delete; + not_null operator++(int) = delete; + not_null operator--(int) = delete; + not_null& operator+=(std::ptrdiff_t) = delete; + not_null& operator-=(std::ptrdiff_t) = delete; + void operator[](std::ptrdiff_t) const = delete; + + void swap(not_null& other) noexcept { std::swap(ptr_, other.ptr_); } + +private: + T ptr_; +}; + +template ::value && + std::is_move_constructible::value, + bool> = true> +void swap(not_null& a, not_null& b) noexcept +{ + a.swap(b); +} + +template +auto make_not_null(T&& t) noexcept +{ + return not_null>>{std::forward(t)}; +} + +#if !defined(GSL_NO_IOSTREAMS) +template +std::ostream& operator<<(std::ostream& os, const not_null& val) +{ + os << val.get(); + return os; +} +#endif // !defined(GSL_NO_IOSTREAMS) + +template +constexpr auto operator==(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(lhs.get() == rhs.get())) + -> decltype(lhs.get() == rhs.get()) +{ + return lhs.get() == rhs.get(); +} + +template +constexpr auto operator!=(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(lhs.get() != rhs.get())) + -> decltype(lhs.get() != rhs.get()) +{ + return lhs.get() != rhs.get(); +} + +template +constexpr auto operator<(const not_null& lhs, const not_null& rhs) noexcept( + noexcept(std::less<>{}(lhs.get(), rhs.get()))) -> decltype(std::less<>{}(lhs.get(), rhs.get())) +{ + return std::less<>{}(lhs.get(), rhs.get()); +} + +template +constexpr auto +operator<=(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(std::less_equal<>{}(lhs.get(), rhs.get()))) + -> decltype(std::less_equal<>{}(lhs.get(), rhs.get())) +{ + return std::less_equal<>{}(lhs.get(), rhs.get()); +} + +template +constexpr auto +operator>(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(std::greater<>{}(lhs.get(), rhs.get()))) + -> decltype(std::greater<>{}(lhs.get(), rhs.get())) +{ + return std::greater<>{}(lhs.get(), rhs.get()); +} + +template +constexpr auto +operator>=(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(std::greater_equal<>{}(lhs.get(), rhs.get()))) + -> decltype(std::greater_equal<>{}(lhs.get(), rhs.get())) +{ + return std::greater_equal<>{}(lhs.get(), rhs.get()); +} + +// more unwanted operators +template +std::ptrdiff_t operator-(const not_null&, const not_null&) = delete; +template +not_null operator-(const not_null&, std::ptrdiff_t) = delete; +template +not_null operator+(const not_null&, std::ptrdiff_t) = delete; +template +not_null operator+(std::ptrdiff_t, const not_null&) = delete; + +// T is conceptually a pointer so we don't have to worry about it being a reference and violating +// std::hash requirements +template >::value> +struct not_null_hash +{ + std::size_t operator()(const T& value) const noexcept { return std::hash{}(value.get()); } +}; + +template +struct not_null_hash +{ + not_null_hash() = delete; + not_null_hash(const not_null_hash&) = delete; + not_null_hash& operator=(const not_null_hash&) = delete; +}; + +} // namespace gsl + +namespace std +{ +template +struct hash> : gsl::not_null_hash> +{ +}; + +} // namespace std + +namespace gsl +{ + +// +// strict_not_null +// +// Restricts a pointer or smart pointer to only hold non-null values, +// +// - provides a strict (i.e. explicit constructor from T) wrapper of not_null +// - to be used for new code that wishes the design to be cleaner and make not_null +// checks intentional, or in old code that would like to make the transition. +// +// To make the transition from not_null, incrementally replace not_null +// by strict_not_null and fix compilation errors +// +// Expect to +// - remove all unneeded conversions from raw pointer to not_null and back +// - make API clear by specifying not_null in parameters where needed +// - remove unnecessary asserts +// +template +class strict_not_null : public not_null +{ +public: + template ::value>> + constexpr explicit strict_not_null(U&& u) noexcept(std::is_nothrow_move_constructible::value) + : not_null(std::forward(u)) + {} + + template ::value>> + constexpr explicit strict_not_null(T u) noexcept(std::is_nothrow_move_constructible::value) + : not_null(std::move(u)) + {} + + template ::value>> + constexpr strict_not_null(const not_null& other) noexcept( + std::is_nothrow_move_constructible::value) + : not_null(other) + {} + + template ::value>> + constexpr strict_not_null(const strict_not_null& other) noexcept( + std::is_nothrow_move_constructible::value) + : not_null(other) + {} + + // To avoid invalidating the "not null" invariant, the contained pointer is actually copied + // instead of moved. If it is a custom pointer, its constructor could in theory throw + // exceptions. + strict_not_null(strict_not_null&& other) noexcept( + std::is_nothrow_copy_constructible::value) = default; + strict_not_null(const strict_not_null& other) = default; + strict_not_null& operator=(const strict_not_null& other) = default; + strict_not_null& operator=(const not_null& other) + { + not_null::operator=(other); + return *this; + } + + // prevents compilation when someone attempts to assign a null pointer constant + strict_not_null(std::nullptr_t) = delete; + strict_not_null& operator=(std::nullptr_t) = delete; + + // unwanted operators...pointers only point to single objects! + strict_not_null& operator++() = delete; + strict_not_null& operator--() = delete; + strict_not_null operator++(int) = delete; + strict_not_null operator--(int) = delete; + strict_not_null& operator+=(std::ptrdiff_t) = delete; + strict_not_null& operator-=(std::ptrdiff_t) = delete; + void operator[](std::ptrdiff_t) const = delete; +}; + +// more unwanted operators +template +std::ptrdiff_t operator-(const strict_not_null&, const strict_not_null&) = delete; +template +strict_not_null operator-(const strict_not_null&, std::ptrdiff_t) = delete; +template +strict_not_null operator+(const strict_not_null&, std::ptrdiff_t) = delete; +template +strict_not_null operator+(std::ptrdiff_t, const strict_not_null&) = delete; + +template +auto make_strict_not_null(T&& t) noexcept +{ + return strict_not_null>>{std::forward(t)}; +} + +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) + +// deduction guides to prevent the ctad-maybe-unsupported warning +template +not_null(T) -> not_null; +template +strict_not_null(T) -> strict_not_null; + +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) + +} // namespace gsl + +namespace std +{ +template +struct hash> : gsl::not_null_hash> +{ +}; + +} // namespace std + +#endif // GSL_POINTERS_H diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/span b/kernel/third_party/GSL-5.0.0/include/gsl/span new file mode 100644 index 0000000..090a8bd --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/span @@ -0,0 +1,853 @@ +// -*- C++ -*- +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_SPAN_H +#define GSL_SPAN_H + +#include "./assert" // for Expects +#include "./byte" // for gsl::impl::byte +#include "./span_ext" // for span specialization of gsl::at and other span-related extensions +#include "./util" // for narrow_cast + +#include // for array +#include // for ptrdiff_t, size_t, nullptr_t +#include // for reverse_iterator, distance, random_access_... +#include // for pointer_traits +#include // for enable_if_t, declval, is_convertible, inte... + +#if defined(__has_include) && __has_include() +#include +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#pragma warning(push) + +// turn off some warnings that are noisy about our Expects statements +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning( \ + disable : 4146) // unary minus operator applied to unsigned type, result still unsigned +#pragma warning(disable : 4702) // unreachable code + +// Turn MSVC /analyze rules that generate too much noise. TODO: fix in the tool. +#pragma warning(disable : 26495) // uninitialized member when constructor calls constructor +#pragma warning(disable : 26446) // parser bug does not allow attributes on some templates + +#endif // _MSC_VER + +// See if we have enough C++17 power to use a static constexpr data member +// without needing an out-of-line definition +#if !(defined(__cplusplus) && (__cplusplus >= 201703L)) +#define GSL_USE_STATIC_CONSTEXPR_WORKAROUND +#endif // !(defined(__cplusplus) && (__cplusplus >= 201703L)) + +// GCC 7 does not like the signed unsigned mismatch (size_t ptrdiff_t) +// While there is a conversion from signed to unsigned, it happens at +// compiletime, so the compiler wouldn't have to warn indiscriminately, but +// could check if the source value actually doesn't fit into the target type +// and only warn in those cases. +#if defined(__GNUC__) && __GNUC__ > 6 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +// Turn off clang unsafe buffer warnings as all accessed are guarded by runtime checks +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif // __has_warning("-Wunsafe-buffer-usage") +#endif // defined(__clang__) + +namespace gsl +{ + +// implementation details +namespace details +{ + template + struct is_span_oracle : std::false_type + { + }; + + template + struct is_span_oracle> : std::true_type + { + }; + + template + struct is_span : public is_span_oracle> + { + }; + + template + struct is_std_array_oracle : std::false_type + { + }; + + template + struct is_std_array_oracle> : std::true_type + { + }; + + template + struct is_std_array : is_std_array_oracle> + { + }; + + template + struct is_allowed_extent_conversion + : std::integral_constant + { + }; + + template + struct is_allowed_element_type_conversion + : std::integral_constant::value> + { + }; + + template + class span_iterator + { + public: +#if defined(__cpp_lib_ranges) || (defined(_MSVC_STL_VERSION) && defined(__cpp_lib_concepts)) + using iterator_concept = std::contiguous_iterator_tag; +#endif // __cpp_lib_ranges + using iterator_category = std::random_access_iterator_tag; + using value_type = std::remove_cv_t; + using difference_type = std::ptrdiff_t; + using pointer = Type*; + using reference = Type&; + +#ifdef _MSC_VER + using _Unchecked_type = pointer; + using _Prevent_inheriting_unwrap = span_iterator; +#endif // _MSC_VER + constexpr span_iterator() = default; + + constexpr operator span_iterator() const noexcept + { + return {begin_, end_, current_}; + } + + constexpr reference operator*() const noexcept + { + Expects(current_ != end_); + return *current_; + } + + constexpr pointer operator->() const noexcept + { + Expects(current_ != end_); + return current_; + } + constexpr span_iterator& operator++() noexcept + { + Expects(current_ != end_); + GSL_SUPPRESS(bounds.1) + ++current_; + return *this; + } + + constexpr span_iterator operator++(int) noexcept + { + span_iterator ret = *this; + ++*this; + return ret; + } + + constexpr span_iterator& operator--() noexcept + { + Expects(begin_ != current_); + --current_; + return *this; + } + + constexpr span_iterator operator--(int) noexcept + { + span_iterator ret = *this; + --*this; + return ret; + } + + constexpr span_iterator& operator+=(const difference_type n) noexcept + { + if (n != 0) Expects(begin_ && current_ && end_); + if (n > 0) Expects(end_ - current_ >= n); + if (n < 0) Expects(current_ - begin_ >= -n); + GSL_SUPPRESS(bounds.1) + current_ += n; + return *this; + } + + constexpr span_iterator operator+(const difference_type n) const noexcept + { + span_iterator ret = *this; + ret += n; + return ret; + } + + friend constexpr span_iterator operator+(const difference_type n, + const span_iterator& rhs) noexcept + { + return rhs + n; + } + + constexpr span_iterator& operator-=(const difference_type n) noexcept + { + if (n != 0) Expects(begin_ && current_ && end_); + if (n > 0) Expects(current_ - begin_ >= n); + if (n < 0) Expects(end_ - current_ >= -n); + GSL_SUPPRESS(bounds.1) + current_ -= n; + return *this; + } + + constexpr span_iterator operator-(const difference_type n) const noexcept + { + span_iterator ret = *this; + ret -= n; + return ret; + } + + template < + class Type2, + std::enable_if_t, value_type>::value, int> = 0> + constexpr difference_type operator-(const span_iterator& rhs) const noexcept + { + Expects(begin_ == rhs.begin_ && end_ == rhs.end_); + return current_ - rhs.current_; + } + + constexpr reference operator[](const difference_type n) const noexcept + { + return *(*this + n); + } + + template < + class Type2, + std::enable_if_t, value_type>::value, int> = 0> + constexpr bool operator==(const span_iterator& rhs) const noexcept + { + Expects(begin_ == rhs.begin_ && end_ == rhs.end_); + return current_ == rhs.current_; + } + + template < + class Type2, + std::enable_if_t, value_type>::value, int> = 0> + constexpr bool operator!=(const span_iterator& rhs) const noexcept + { + return !(*this == rhs); + } + + template < + class Type2, + std::enable_if_t, value_type>::value, int> = 0> + constexpr bool operator<(const span_iterator& rhs) const noexcept + { + Expects(begin_ == rhs.begin_ && end_ == rhs.end_); + return current_ < rhs.current_; + } + + template < + class Type2, + std::enable_if_t, value_type>::value, int> = 0> + constexpr bool operator>(const span_iterator& rhs) const noexcept + { + return rhs < *this; + } + + template < + class Type2, + std::enable_if_t, value_type>::value, int> = 0> + constexpr bool operator<=(const span_iterator& rhs) const noexcept + { + return !(rhs < *this); + } + + template < + class Type2, + std::enable_if_t, value_type>::value, int> = 0> + constexpr bool operator>=(const span_iterator& rhs) const noexcept + { + return !(*this < rhs); + } + +#ifdef _MSC_VER + // MSVC++ iterator debugging support; allows STL algorithms in 15.8+ + // to unwrap span_iterator to a pointer type after a range check in STL + // algorithm calls + friend constexpr void _Verify_range(span_iterator lhs, span_iterator rhs) noexcept + { // test that [lhs, rhs) forms a valid range inside an STL algorithm + Expects(lhs.begin_ == rhs.begin_ // range spans have to match + && lhs.end_ == rhs.end_ && + lhs.current_ <= rhs.current_); // range must not be transposed + } + + constexpr void _Verify_offset(const difference_type n) const noexcept + { // test that *this + n is within the range of this call + if (n != 0) Expects(begin_ && current_ && end_); + if (n > 0) Expects(end_ - current_ >= n); + if (n < 0) Expects(current_ - begin_ >= -n); + } + + GSL_SUPPRESS(bounds.1) + constexpr pointer _Unwrapped() const noexcept + { // after seeking *this to a high water mark, or using one of the + // _Verify_xxx functions above, unwrap this span_iterator to a raw + // pointer + return current_; + } + + // Tell the STL that span_iterator should not be unwrapped if it can't + // validate in advance, even in release / optimized builds: +#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) + static constexpr const bool _Unwrap_when_unverified = false; +#else + static constexpr bool _Unwrap_when_unverified = false; +#endif + GSL_SUPPRESS(con.3) // TODO: false positive + constexpr void _Seek_to(const pointer p) noexcept + { // adjust the position of *this to previously verified location p + // after _Unwrapped + current_ = p; + } +#endif + + private: + constexpr span_iterator(pointer begin, pointer end, pointer current) + : begin_(begin), end_(end), current_(current) + { + Expects(begin_ <= current_ && current <= end_); + } + + pointer begin_ = nullptr; + pointer end_ = nullptr; + pointer current_ = nullptr; + + template + friend class span_iterator; + template + friend class ::gsl::span; + template + friend struct std::pointer_traits; + }; +} // namespace details +} // namespace gsl + +namespace std +{ +template +struct pointer_traits<::gsl::details::span_iterator> +{ + using pointer = ::gsl::details::span_iterator; + using element_type = Type; + using difference_type = ptrdiff_t; + + static constexpr element_type* to_address(const pointer i) noexcept { return i.current_; } +}; +} // namespace std + +namespace gsl +{ +namespace details +{ + template + class extent_type + { + public: + using size_type = std::size_t; + + constexpr extent_type() noexcept = default; + + constexpr explicit extent_type(extent_type); + + constexpr explicit extent_type(size_type size) { Expects(size == Ext); } + + constexpr size_type size() const noexcept { return Ext; } + + private: +#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) + static constexpr const size_type size_ = Ext; // static size equal to Ext +#else + static constexpr size_type size_ = Ext; // static size equal to Ext +#endif + }; + + template <> + class extent_type + { + public: + using size_type = std::size_t; + + template + constexpr explicit extent_type(extent_type ext) : size_(ext.size()) + {} + + constexpr explicit extent_type(size_type size) : size_(size) + { + Expects(size != dynamic_extent); + } + + constexpr size_type size() const noexcept { return size_; } + + private: + size_type size_; + }; + + template + constexpr extent_type::extent_type(extent_type ext) + { + Expects(ext.size() == Ext); + } + + template + struct calculate_subspan_type + { + using type = span; + }; +} // namespace details + +// [span], class template span +template +class span +{ +public: + // constants and types + using element_type = ElementType; + using value_type = std::remove_cv_t; + using size_type = std::size_t; + using pointer = element_type*; + using const_pointer = const element_type*; + using reference = element_type&; + using const_reference = const element_type&; + using difference_type = std::ptrdiff_t; + + using iterator = details::span_iterator; + using reverse_iterator = std::reverse_iterator; + +#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) + static constexpr const size_type extent{Extent}; +#else + static constexpr size_type extent{Extent}; +#endif + + // [span.cons], span constructors, copy, assignment, and destructor + template " SFINAE, since "std::enable_if_t" is ill-formed when Extent is greater than 0. + class = std::enable_if_t<(Dependent || + details::is_allowed_extent_conversion<0, Extent>::value)>> + constexpr span() noexcept : storage_(nullptr, details::extent_type<0>()) + {} + + template = 0> + constexpr explicit span(pointer ptr, size_type count) noexcept : storage_(ptr, count) + { + Expects(count == Extent); + } + + template = 0> + constexpr span(pointer ptr, size_type count) noexcept : storage_(ptr, count) + {} + + template = 0> + constexpr explicit span(pointer firstElem, pointer lastElem) noexcept + : storage_(firstElem, narrow_cast(lastElem - firstElem)) + { + Expects(lastElem - firstElem == static_cast(Extent)); + } + + template = 0> + constexpr span(pointer firstElem, pointer lastElem) noexcept + : storage_(firstElem, narrow_cast(lastElem - firstElem)) + {} + + template ::value, int> = 0> + constexpr span(element_type (&arr)[N]) noexcept + : storage_(KnownNotNull{arr}, details::extent_type()) + {} + + template < + class T, std::size_t N, + std::enable_if_t<(details::is_allowed_extent_conversion::value && + details::is_allowed_element_type_conversion::value), + int> = 0> + constexpr span(std::array& arr) noexcept + : storage_(KnownNotNull{arr.data()}, details::extent_type()) + {} + + template ::value && + details::is_allowed_element_type_conversion::value), + int> = 0> + constexpr span(const std::array& arr) noexcept + : storage_(KnownNotNull{arr.data()}, details::extent_type()) + {} + + // NB: the SFINAE on these constructors uses .data() as an incomplete/imperfect proxy for the + // requirement on Container to be a contiguous sequence container. + template ::value && + !details::is_std_array::value && + std::is_pointer().data())>::value && + std::is_convertible< + std::remove_pointer_t().data())> (*)[], + element_type (*)[]>::value, + int> = 0> + constexpr explicit span(Container& cont) noexcept : span(cont.data(), cont.size()) + {} + + template ::value && + !details::is_std_array::value && + std::is_pointer().data())>::value && + std::is_convertible< + std::remove_pointer_t().data())> (*)[], + element_type (*)[]>::value, + int> = 0> + constexpr span(Container& cont) noexcept : span(cont.data(), cont.size()) + {} + + template < + std::size_t MyExtent = Extent, class Container, + std::enable_if_t< + MyExtent != dynamic_extent && std::is_const::value && + !details::is_span::value && !details::is_std_array::value && + std::is_pointer().data())>::value && + std::is_convertible< + std::remove_pointer_t().data())> (*)[], + element_type (*)[]>::value, + int> = 0> + constexpr explicit span(const Container& cont) noexcept : span(cont.data(), cont.size()) + {} + + template < + std::size_t MyExtent = Extent, class Container, + std::enable_if_t< + MyExtent == dynamic_extent && std::is_const::value && + !details::is_span::value && !details::is_std_array::value && + std::is_pointer().data())>::value && + std::is_convertible< + std::remove_pointer_t().data())> (*)[], + element_type (*)[]>::value, + int> = 0> + constexpr span(const Container& cont) noexcept : span(cont.data(), cont.size()) + {} + + constexpr span(const span& other) noexcept = default; + + template ::value, + int> = 0> + constexpr span(const span& other) noexcept + : storage_(other.data(), details::extent_type(other.size())) + {} + + template ::value, + int> = 0> + constexpr explicit span(const span& other) noexcept + : storage_(other.data(), details::extent_type(other.size())) + {} + + ~span() noexcept = default; + constexpr span& operator=(const span& other) noexcept = default; + + // [span.sub], span subviews + template + constexpr span first() const noexcept + { + static_assert(Extent == dynamic_extent || Count <= Extent, + "first() cannot extract more elements from a span than it contains."); + Expects(Count <= size()); + return span{data(), Count}; + } + + template + GSL_SUPPRESS(bounds.1) constexpr span last() const noexcept + { + static_assert(Extent == dynamic_extent || Count <= Extent, + "last() cannot extract more elements from a span than it contains."); + Expects(Count <= size()); + return span{data() + (size() - Count), Count}; + } + + template + GSL_SUPPRESS(bounds.1) constexpr auto subspan() const noexcept -> + typename details::calculate_subspan_type::type + { + static_assert(Extent == dynamic_extent || (Extent >= Offset && (Count == dynamic_extent || + Count <= Extent - Offset)), + "subspan() cannot extract more elements from a span than it contains."); + Expects((size() >= Offset) && (Count == dynamic_extent || (Count <= size() - Offset))); + using type = + typename details::calculate_subspan_type::type; + return type{data() + Offset, Count == dynamic_extent ? size() - Offset : Count}; + } + + constexpr span first(size_type count) const noexcept + { + Expects(count <= size()); + return {data(), count}; + } + + constexpr span last(size_type count) const noexcept + { + Expects(count <= size()); + return make_subspan(size() - count, dynamic_extent, subspan_selector{}); + } + + constexpr span + subspan(size_type offset, size_type count = dynamic_extent) const noexcept + { + return make_subspan(offset, count, subspan_selector{}); + } + + // [span.obs], span observers + constexpr size_type size() const noexcept { return storage_.size(); } + + constexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); } + + constexpr bool empty() const noexcept { return size() == 0; } + + // [span.elem], span element access + GSL_SUPPRESS(bounds.1) + constexpr reference operator[](size_type idx) const noexcept + { + Expects(idx < size()); + return data()[idx]; + } + + constexpr reference front() const noexcept + { + Expects(size() > 0); + return data()[0]; + } + + constexpr reference back() const noexcept + { + Expects(size() > 0); + return data()[size() - 1]; + } + + constexpr pointer data() const noexcept { return storage_.data(); } + + // [span.iter], span iterator support + constexpr iterator begin() const noexcept + { + const auto data = storage_.data(); + GSL_SUPPRESS(bounds.1) + return {data, data + size(), data}; + } + + constexpr iterator end() const noexcept + { + const auto data = storage_.data(); + GSL_SUPPRESS(bounds.1) + const auto endData = data + storage_.size(); + return {data, endData, endData}; + } + + constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } + constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } + +#ifdef _MSC_VER + // Tell MSVC how to unwrap spans in range-based-for + constexpr pointer _Unchecked_begin() const noexcept { return data(); } + constexpr pointer _Unchecked_end() const noexcept + { + GSL_SUPPRESS(bounds.1) + return data() + size(); + } +#endif // _MSC_VER + +private: + // Needed to remove unnecessary null check in subspans + struct KnownNotNull + { + pointer p; + }; + + // this implementation detail class lets us take advantage of the + // empty base class optimization to pay for only storage of a single + // pointer in the case of fixed-size spans + template + class storage_type : public ExtentType + { + public: + // KnownNotNull parameter is needed to remove unnecessary null check + // in subspans and constructors from arrays + template + constexpr storage_type(KnownNotNull data, OtherExtentType ext) + : ExtentType(ext), data_(data.p) + {} + + template + constexpr storage_type(pointer data, OtherExtentType ext) : ExtentType(ext), data_(data) + { + Expects(data || ExtentType::size() == 0); + } + + constexpr pointer data() const noexcept { return data_; } + + private: + pointer data_; + }; + + storage_type> storage_; + + // The rest is needed to remove unnecessary null check + // in subspans and constructors from arrays + constexpr span(KnownNotNull ptr, size_type count) noexcept : storage_(ptr, count) {} + + template + class subspan_selector + { + }; + + template + constexpr span + make_subspan(size_type offset, size_type count, subspan_selector) const noexcept + { + const span tmp(*this); + return tmp.subspan(offset, count); + } + + GSL_SUPPRESS(bounds.1) + constexpr span + make_subspan(size_type offset, size_type count, subspan_selector) const noexcept + { + Expects(size() >= offset); + + if (count == dynamic_extent) { return {KnownNotNull{data() + offset}, size() - offset}; } + + Expects(size() - offset >= count); + return {KnownNotNull{data() + offset}, count}; + } +}; + +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) + +// Deduction Guides +template +span(Type (&)[Extent]) -> span; + +template +span(std::array&) -> span; + +template +span(const std::array&) -> span; + +template ().data())>> +span(Container&) -> span; + +template ().data())>> +span(const Container&) -> span; + +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) + +#if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) +#if defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L) +#pragma clang diagnostic push +#pragma clang diagnostic ignored \ + "-Wdeprecated" // Bug in clang-cl.exe which raises a C++17 -Wdeprecated warning about this + // static constexpr workaround in C++14 mode. +#endif // defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L) +template +constexpr const typename span::size_type span::extent; +#if defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L) +#pragma clang diagnostic pop +#endif // defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L) +#endif + +namespace details +{ + // if we only supported compilers with good constexpr support then + // this pair of classes could collapse down to a constexpr function + + // we should use a narrow_cast<> to go to std::size_t, but older compilers may not see it as + // constexpr + // and so will fail compilation of the template + template + struct calculate_byte_size : std::integral_constant + { + static_assert(Extent < dynamic_extent / sizeof(ElementType), "Size is too big."); + }; + + template + struct calculate_byte_size + : std::integral_constant + { + }; +} // namespace details + +// [span.objectrep], views of object representation +template +span::value> +as_bytes(span s) noexcept +{ + using type = + span::value>; + + GSL_SUPPRESS(type.1) + return type{reinterpret_cast(s.data()), s.size_bytes()}; +} + +template ::value, int> = 0> +span::value> +as_writable_bytes(span s) noexcept +{ + using type = span::value>; + + GSL_SUPPRESS(type.1) + return type{reinterpret_cast(s.data()), s.size_bytes()}; +} + +} // namespace gsl + +#if defined(_MSC_VER) && !defined(__clang__) + +#pragma warning(pop) +#endif // _MSC_VER + +#if defined(__GNUC__) && __GNUC__ > 6 +#pragma GCC diagnostic pop +#endif // __GNUC__ > 6 + +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang diagnostic pop +#endif // __has_warning("-Wunsafe-buffer-usage") +#endif // defined(__clang__) + +#endif // GSL_SPAN_H diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/span_ext b/kernel/third_party/GSL-5.0.0/include/gsl/span_ext new file mode 100644 index 0000000..d9c0e98 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/span_ext @@ -0,0 +1,214 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_SPAN_EXT_H +#define GSL_SPAN_EXT_H + +/////////////////////////////////////////////////////////////////////////////// +// +// File: span_ext +// Purpose: continue offering features that have been cut from the official +// implementation of span. +// While modernizing gsl::span a number of features needed to be removed to +// be compliant with the design of std::span +// +/////////////////////////////////////////////////////////////////////////////// + +#include "./assert" // GSL_KERNEL_MODE +#include "./util" // for narrow_cast, narrow + +#include // for ptrdiff_t, size_t +#include + +#ifndef GSL_KERNEL_MODE +#include // for lexicographical_compare +#endif // GSL_KERNEL_MODE + +namespace gsl +{ + +// [span.views.constants], constants +GSL_INLINE constexpr const std::size_t dynamic_extent = narrow_cast(-1); + +template +class span; + +// std::equal and std::lexicographical_compare are not /kernel compatible +// so all comparison operators must be removed for kernel mode. +#ifndef GSL_KERNEL_MODE + +// [span.comparison], span comparison operators +template +constexpr bool operator==(span l, span r) +{ + return std::equal(l.begin(), l.end(), r.begin(), r.end()); +} + +template +constexpr bool operator!=(span l, span r) +{ + return !(l == r); +} + +template +constexpr bool operator<(span l, span r) +{ + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); +} + +template +constexpr bool operator<=(span l, span r) +{ + return !(l > r); +} + +template +constexpr bool operator>(span l, span r) +{ + return r < l; +} + +template +constexpr bool operator>=(span l, span r) +{ + return !(l < r); +} + +#endif // GSL_KERNEL_MODE + +// +// make_span() - Utility functions for creating spans +// +template +constexpr span make_span(ElementType* ptr, typename span::size_type count) +{ + return span(ptr, count); +} + +template +constexpr span make_span(ElementType* firstElem, ElementType* lastElem) +{ + return span(firstElem, lastElem); +} + +template +constexpr span make_span(ElementType (&arr)[N]) noexcept +{ + return span(arr); +} + +template +constexpr span make_span(Container& cont) +{ + return span(cont); +} + +template +constexpr span make_span(const Container& cont) +{ + return span(cont); +} + +template +GSL_DEPRECATED("This function is deprecated. See GSL issue #1092.") +constexpr span make_span(Ptr& cont, std::size_t count) +{ + return span(cont, count); +} + +template +GSL_DEPRECATED("This function is deprecated. See GSL issue #1092.") +constexpr span make_span(Ptr& cont) +{ + return span(cont); +} + +// Specialization of gsl::at for span +template +constexpr ElementType& at(span s, index i) +{ + // No bounds checking here because it is done in span::operator[] called below + Ensures(i >= 0); + return s[narrow_cast(i)]; +} + +// [span.obs] Free observer functions +template +constexpr std::ptrdiff_t ssize(const span& s) noexcept +{ + return gsl::narrow_cast(s.size()); +} + +// [span.iter] Free functions for begin/end functions +template +constexpr typename span::iterator +begin(const span& s) noexcept +{ + return s.begin(); +} + +template +constexpr typename span::iterator +end(const span& s) noexcept +{ + return s.end(); +} + +template +constexpr typename span::reverse_iterator +rbegin(const span& s) noexcept +{ + return s.rbegin(); +} + +template +constexpr typename span::reverse_iterator +rend(const span& s) noexcept +{ + return s.rend(); +} + +template +constexpr typename span::iterator +cbegin(const span& s) noexcept +{ + return s.begin(); +} + +template +constexpr typename span::iterator +cend(const span& s) noexcept +{ + return s.end(); +} + +template +constexpr typename span::reverse_iterator +crbegin(const span& s) noexcept +{ + return s.rbegin(); +} + +template +constexpr typename span::reverse_iterator +crend(const span& s) noexcept +{ + return s.rend(); +} + +} // namespace gsl + +#endif // GSL_SPAN_EXT_H diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/util b/kernel/third_party/GSL-5.0.0/include/gsl/util new file mode 100644 index 0000000..2ce264d --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/util @@ -0,0 +1,248 @@ +// -*- C++ -*- +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_UTIL_H +#define GSL_UTIL_H + +#include "./assert" // for Expects + +#include // for ptrdiff_t, size_t +#include // for initializer_list +#include // for iterator_traits +#include // for numeric_limits +#include // for is_signed, integral_constant +#include // for exchange, forward + +#if defined(__has_include) && __has_include() +#include +#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L +#include +#endif // __cpp_lib_span >= 202002L +#endif //__has_include() + +#if defined(_MSC_VER) && !defined(__clang__) + +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant + +#endif // _MSC_VER + +// Turn off clang unsafe buffer warnings as all accessed are guarded by runtime checks +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif // __has_warning("-Wunsafe-buffer-usage") +#endif // defined(__clang__) + +#if defined(__cplusplus) && (__cplusplus >= 201703L) +#define GSL_NODISCARD [[nodiscard]] +#else +#define GSL_NODISCARD +#endif // defined(__cplusplus) && (__cplusplus >= 201703L) + +#if defined(__cpp_inline_variables) +#define GSL_INLINE inline +#else +#define GSL_INLINE +#endif + +#if defined(__has_cpp_attribute) +#if __has_cpp_attribute(deprecated) +#define GSL_DEPRECATED(msg) [[deprecated(msg)]] +#endif // __has_cpp_attribute(deprecated) +#endif // defined(__has_cpp_attribute) + +#if !defined(GSL_DEPRECATED) +#if defined(__cplusplus) +#if __cplusplus >= 201309L +#define GSL_DEPRECATED(msg) [[deprecated(msg)]] +#endif // __cplusplus >= 201309L +#endif // defined(__cplusplus) +#endif // !defined(GSL_DEPRECATED) + +#if !defined(GSL_DEPRECATED) +#if defined(_MSC_VER) +#define GSL_DEPRECATED(msg) __declspec(deprecated(msg)) +#elif defined(__GNUC__) +#define GSL_DEPRECATED(msg) __attribute__((deprecated(msg))) +#endif // defined(_MSC_VER) +#endif // !defined(GSL_DEPRECATED) + +#if !defined(GSL_DEPRECATED) +#define GSL_DEPRECATED(msg) +#endif // !defined(GSL_DEPRECATED) + +#if __cplusplus >= 202002L +#define GSL_CONSTEXPR_SINCE_CPP20 constexpr +#else // ^^^ since C++20 /// before C++20 vvv +#define GSL_BEFORE_CPP20 +#define GSL_CONSTEXPR_SINCE_CPP20 +#endif // __cplusplus >= 202002L + +namespace gsl +{ +// +// GSL.util: utilities +// + +// index type for all container indexes/subscripts/sizes +using index = std::ptrdiff_t; + +namespace details +{ + template + using void_t = void; + + template + struct is_iterator : std::false_type + { + }; + + template + struct is_iterator::value_type>> : std::true_type + { + }; + + template + struct is_fwd_iterator : std::false_type + { + }; + + template + struct is_fwd_iterator::iterator_category>> + : std::integral_constant< + bool, std::is_base_of::iterator_category>::value> + { + }; +} // namespace details + +// final_action allows you to ensure something gets run at the end of a scope +// The bool member causes trailing padding when F has alignment > 1; suppress +// -Wpadded since the padding is unavoidable for a generic callable wrapper. +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif // defined(__clang__) +template +class final_action +{ +public: + explicit final_action(const F& ff) noexcept : f{ff} {} + explicit final_action(F&& ff) noexcept : f{std::move(ff)} {} + + ~final_action() noexcept + { + if (invoke) f(); + } + + final_action(final_action&& other) noexcept + : f(std::move(other.f)), invoke(std::exchange(other.invoke, false)) + {} + + final_action(const final_action&) = delete; + void operator=(const final_action&) = delete; + void operator=(final_action&&) = delete; + +private: + F f; + bool invoke = true; +}; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // defined(__clang__) + +// finally() - convenience function to generate a final_action +template +GSL_NODISCARD auto finally(F&& f) noexcept +{ + return final_action>{std::forward(f)}; +} + +// narrow_cast(): a searchable way to do narrowing casts of values +template +GSL_SUPPRESS(type.1) constexpr T narrow_cast(U&& u) noexcept +{ +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + return static_cast(std::forward(u)); +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +} + +// +// at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector +// +template +GSL_SUPPRESS(bounds.4) GSL_SUPPRESS(bounds.2) constexpr T& at(T (&arr)[N], const index i) +{ + static_assert(N <= static_cast((std::numeric_limits::max)()), + "We only support arrays up to PTRDIFF_MAX bytes."); + Expects(i >= 0 && i < narrow_cast(N)); + return arr[narrow_cast(i)]; +} + +template +GSL_SUPPRESS(bounds.4) GSL_SUPPRESS(bounds.2) constexpr auto at(Cont& cont, const index i) + -> decltype(cont[cont.size()]) +{ + Expects(i >= 0 && i < narrow_cast(cont.size())); + using size_type = decltype(cont.size()); + return cont[narrow_cast(i)]; +} + +template +GSL_SUPPRESS(bounds.1) constexpr T at(const std::initializer_list cont, const index i) +{ + Expects(i >= 0 && i < narrow_cast(cont.size())); + return *(cont.begin() + i); +} + +template ::value && + std::is_move_constructible::value>> +void swap(T& a, T& b) +{ + std::swap(a, b); +} + +#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L +template +constexpr auto at(std::span sp, const index i) -> decltype(sp[sp.size()]) +{ + Expects(i >= 0 && i < narrow_cast(sp.size())); + return sp[gsl::narrow_cast(i)]; +} +#endif // __cpp_lib_span >= 202002L +} // namespace gsl + +#if defined(_MSC_VER) && !defined(__clang__) + +#pragma warning(pop) + +#endif // _MSC_VER + +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#pragma clang diagnostic pop +#endif // __has_warning("-Wunsafe-buffer-usage") +#endif // defined(__clang__) + +#endif // GSL_UTIL_H diff --git a/kernel/third_party/GSL-5.0.0/include/gsl/zstring b/kernel/third_party/GSL-5.0.0/include/gsl/zstring new file mode 100644 index 0000000..a718251 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/include/gsl/zstring @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef GSL_ZSTRING_H +#define GSL_ZSTRING_H + +#include "./span_ext" // for dynamic_extent + +#include // for size_t, nullptr_t + +namespace gsl +{ +// +// czstring and wzstring +// +// These are "tag" typedefs for C-style strings (i.e. null-terminated character arrays) +// that allow static analysis to help find bugs. +// +// There are no additional features/semantics that we can find a way to add inside the +// type system for these types that will not either incur significant runtime costs or +// (sometimes needlessly) break existing programs when introduced. +// + +template +using basic_zstring = CharT*; + +using czstring = basic_zstring; + +using cwzstring = basic_zstring; + +using cu16zstring = basic_zstring; + +using cu32zstring = basic_zstring; + +using zstring = basic_zstring; + +using wzstring = basic_zstring; + +using u16zstring = basic_zstring; + +using u32zstring = basic_zstring; + +} // namespace gsl + +#endif // GSL_ZSTRING_H diff --git a/kernel/third_party/GSL-5.0.0/scripts/apply-formatting.bat b/kernel/third_party/GSL-5.0.0/scripts/apply-formatting.bat new file mode 100644 index 0000000..397dccf --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/scripts/apply-formatting.bat @@ -0,0 +1,13 @@ +@echo off +setlocal enabledelayedexpansion + +"%VCINSTALLDIR%Tools\Llvm\bin\clang-format" -version +if %errorlevel% neq 0 ( + echo [ERROR] clang-format not found, script should be called from a visual studio developer command prompt. + exit /b %errorlevel% +) + +for %%f in (include\gsl\* tests\*.h tests\*.cpp) do ( + echo formatting %%f + "%VCINSTALLDIR%Tools\Llvm\bin\clang-format" -i --assume-filename x.cpp "%%f" +) diff --git a/kernel/third_party/GSL-5.0.0/scripts/apply-formatting.sh b/kernel/third_party/GSL-5.0.0/scripts/apply-formatting.sh new file mode 100644 index 0000000..560d9b5 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/scripts/apply-formatting.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euo pipefail + +cf=clang-format-$(grep -i 'CLANG_VERSION:' .github/workflows/clang-format.yml | sed -E 's/.*"([^"]+)".*/\1/') +readonly cf + +if ! "${cf}" -version; then + echo "[ERROR] clang-format not found. Please install it using: sudo apt install ${cf}" + exit 1 +fi + +{ + find include/gsl -type f + find tests -type f \( -name '*.cpp' -o -name '*.h' \) +} | xargs "${cf}" -i --assume-filename=x.cpp --verbose + +find scripts -type f -name '*.sh' -print -exec shfmt -w {} \; diff --git a/kernel/third_party/GSL-5.0.0/tests/CMakeLists.txt b/kernel/third_party/GSL-5.0.0/tests/CMakeLists.txt new file mode 100644 index 0000000..82e84cb --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/CMakeLists.txt @@ -0,0 +1,319 @@ +cmake_minimum_required(VERSION 3.14...3.16) + +project(GSLTests LANGUAGES CXX) + +set(GSL_CXX_STANDARD "14" CACHE STRING "Use c++ standard") + +set(CMAKE_CXX_STANDARD ${GSL_CXX_STANDARD}) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include(FindPkgConfig) +include(ExternalProject) + +# will make visual studio generated project group files +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +if(CI_TESTING AND GSL_CXX_STANDARD EQUAL 20) + add_compile_definitions(FORCE_STD_SPAN_TESTS=1) +endif() + +pkg_search_module(GTestMain gtest_main) +if (NOT GTestMain_FOUND) + # No pre-installed GTest is available, try to download it using Git. + find_package(Git REQUIRED QUIET) + + configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) + execute_process( + COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download + ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + + execute_process( + COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download + ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + set(GTestMain_LIBRARIES gtest_main) + + add_subdirectory( + ${CMAKE_CURRENT_BINARY_DIR}/googletest-src + ${CMAKE_CURRENT_BINARY_DIR}/googletest-build + EXCLUDE_FROM_ALL + ) + + # googletest is built as its own target, so apply this workaround there. + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC" + AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 22) + target_compile_options(gtest PRIVATE -Wno-character-conversion) + target_compile_options(gtest_main PRIVATE -Wno-character-conversion) + endif() +endif() + +if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + find_package(Microsoft.GSL CONFIG REQUIRED) + enable_testing() + + if (NOT DEFINED Microsoft.GSL_VERSION) + message(FATAL_ERROR "Microsoft.GSL_VERSION not defined!") + endif() + + message(STATUS "Microsoft.GSL_VERSION = ${Microsoft.GSL_VERSION}") +endif() + +if (MSVC AND (GSL_CXX_STANDARD GREATER_EQUAL 17)) + set(GSL_CPLUSPLUS_OPT -Zc:__cplusplus -permissive-) +endif() + +include(CheckCXXCompilerFlag) +# this interface adds compile options to how the tests are run +# please try to keep entries ordered =) +add_library(gsl_tests_config INTERFACE) +if(MSVC) # MSVC or simulating MSVC + target_compile_options(gsl_tests_config INTERFACE + ${GSL_CPLUSPLUS_OPT} + /EHsc + /W4 + /WX + $<$: + /wd4996 # Use of function or classes marked [[deprecated]] + /wd26409 # CppCoreCheck - GTest + /wd26426 # CppCoreCheck - GTest + /wd26440 # CppCoreCheck - GTest + /wd26446 # CppCoreCheck - prefer gsl::at() + /wd26472 # CppCoreCheck - use gsl::narrow(_cast) + /wd26481 # CppCoreCheck - use span instead of pointer arithmetic + $<$,1920>: # VS2015 + /wd4189 # variable is initialized but not referenced + $<$>: # Release, RelWithDebInfo + /wd4702 # Unreachable code + > + > + > + $<$: + -Weverything + -Wfloat-equal + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-covered-switch-default # GTest + -Wno-deprecated-declarations # Allow tests for [[deprecated]] elements + -Wno-global-constructors # GTest + -Wno-language-extension-token # GTest gtest-port.h + -Wno-missing-braces + -Wno-missing-prototypes + -Wno-shift-sign-overflow # GTest gtest-port.h + -Wno-undef # GTest + -Wno-used-but-marked-unused # GTest EXPECT_DEATH + -Wno-switch-default # GTest EXPECT_DEATH + $<$: # no support for [[maybe_unused]] + -Wno-unused-member-function + -Wno-unused-variable + $<$,15.0.1>: + -Wno-deprecated # False positive in MSVC Clang 15.0.1 raises a C++17 warning + > + > + > + ) + check_cxx_compiler_flag("-Wno-reserved-identifier" WARN_RESERVED_ID) + if (WARN_RESERVED_ID) + target_compile_options(gsl_tests_config INTERFACE "-Wno-reserved-identifier") + endif() +else() + target_compile_options(gsl_tests_config INTERFACE + -fno-strict-aliasing + -Wall + -Wcast-align + -Wconversion + -Wctor-dtor-privacy + -Werror + -Wextra + -Wpedantic + -Wshadow + -Wsign-conversion + -Wfloat-equal + -Wno-deprecated-declarations # Allow tests for [[deprecated]] elements + $<$,$>: + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-missing-braces + -Wno-covered-switch-default # GTest + -Wno-global-constructors # GTest + -Wno-missing-prototypes + -Wno-padded + -Wno-switch-default + -Wno-unknown-attributes + -Wno-used-but-marked-unused # GTest EXPECT_DEATH + -Wno-weak-vtables + -Wno-poison-system-directories + $<$: # no support for [[maybe_unused]] + -Wno-unused-member-function + -Wno-unused-variable + > + > + $<$: + $<$,4.99>,$,6>>: + $<$:-Wno-undefined-func-template> + > + $<$,$,$>>: + -Wno-zero-as-null-pointer-constant # failing Clang Ubuntu 20.04 tests, seems to be a bug with clang 10.0.0 + # and clang 11.0.0. (operator< is being re-written by the compiler + # as operator<=> and raising the warning) + > + > + $<$: + $<$,9.1>,$,10>>: + $<$:-Wno-undefined-func-template> + > + > + $<$: + -Wdouble-promotion # float implicit to double + -Wlogical-op # suspicious uses of logical operators + $<$,6>>: + -Wduplicated-cond # duplicated if-else conditions + -Wmisleading-indentation + -Wnull-dereference + $<$: # no support for [[maybe_unused]] + -Wno-unused-variable + > + > + $<$,7>>: + -Wduplicated-branches # identical if-else branches + > + > + ) +endif(MSVC) +check_cxx_compiler_flag("-Wno-unsafe-buffer-usage" WARN_UNSAFE_BUFFER) +if (WARN_UNSAFE_BUFFER) + # This test uses very greedy heuristics such as "no pointer arithmetic on raw buffer" + target_compile_options(gsl_tests_config INTERFACE "-Wno-unsafe-buffer-usage") +endif() + +# for tests to find the gtest header +target_include_directories(gsl_tests_config SYSTEM INTERFACE + googletest/googletest/include +) + +# Individually build and register each test source (except no_exception_ensure_tests.cpp) +# no_exception_ensure_tests.cpp is built separately with exceptions disabled +file(GLOB GSL_TEST_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +list(FILTER GSL_TEST_SOURCES EXCLUDE REGEX "no_exception_ensure_tests\\.cpp$") + +foreach(src IN LISTS GSL_TEST_SOURCES) + get_filename_component(test_name "${src}" NAME_WE) + add_executable(${test_name} ${src}) + target_link_libraries(${test_name} + Microsoft.GSL::GSL + gsl_tests_config + ${GTestMain_LIBRARIES} + ) + add_test(NAME ${test_name} COMMAND ${test_name}) + set_target_properties(${test_name} PROPERTIES FOLDER "tests") +endforeach() + +# No exception tests + +foreach(flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + STRING (REGEX REPLACE "/EHsc" "" ${flag_var} "${${flag_var}}") +endforeach(flag_var) + +# this interface adds compile options to how the tests are run +# please try to keep entries ordered =) +add_library(gsl_tests_config_noexcept INTERFACE) +if(MSVC) # MSVC or simulating MSVC + target_compile_definitions(gsl_tests_config_noexcept INTERFACE + _HAS_EXCEPTIONS=0 # disable exceptions in the Microsoft STL + ) + target_compile_options(gsl_tests_config_noexcept INTERFACE + ${GSL_CPLUSPLUS_OPT} + /W4 + /WX + $<$: + /wd4577 + /wd4702 + /wd26440 # CppCoreCheck - GTest + /wd26446 # CppCoreCheck - prefer gsl::at() + > + $<$: + -Weverything + -Wfloat-equal + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-missing-prototypes + -Wno-unknown-attributes + $<$: + $<$,15.0.1>: + -Wno-deprecated # False positive in MSVC Clang 15.0.1 raises a C++17 warning + > + > + > + ) + check_cxx_compiler_flag("-Wno-reserved-identifier" WARN_RESERVED_ID) + if (WARN_RESERVED_ID) + target_compile_options(gsl_tests_config_noexcept INTERFACE "-Wno-reserved-identifier") + endif() +else() + target_compile_options(gsl_tests_config_noexcept INTERFACE + -fno-exceptions + -fno-strict-aliasing + -Wall + -Wcast-align + -Wconversion + -Wctor-dtor-privacy + -Werror + -Wextra + -Wpedantic + -Wshadow + -Wsign-conversion + -Wfloat-equal + $<$,$>: + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-missing-prototypes + -Wno-unknown-attributes + -Wno-weak-vtables + -Wno-poison-system-directories + > + $<$: + -Wdouble-promotion # float implicit to double + -Wlogical-op # suspicious uses of logical operators + -Wuseless-cast # casting to its own type + $<$,6>>: + -Wduplicated-cond # duplicated if-else conditions + -Wmisleading-indentation + -Wnull-dereference + > + $<$,7>>: + -Wduplicated-branches # identical if-else branches + > + $<$,8>>: + -Wcast-align=strict # increase alignment (i.e. char* to int*) + > + > + ) +endif(MSVC) +check_cxx_compiler_flag("-Wno-unsafe-buffer-usage" WARN_UNSAFE_BUFFER) +if (WARN_UNSAFE_BUFFER) + # This test uses very greedy heuristics such as "no pointer arithmetic on raw buffer" + target_compile_options(gsl_tests_config_noexcept INTERFACE "-Wno-unsafe-buffer-usage") +endif() + +add_executable(gsl_noexcept_tests no_exception_ensure_tests.cpp) +target_link_libraries(gsl_noexcept_tests + Microsoft.GSL::GSL + gsl_tests_config_noexcept +) +add_test(gsl_noexcept_tests gsl_noexcept_tests) diff --git a/kernel/third_party/GSL-5.0.0/tests/CMakeLists.txt.in b/kernel/third_party/GSL-5.0.0/tests/CMakeLists.txt.in new file mode 100644 index 0000000..4f919f6 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/CMakeLists.txt.in @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.13) +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.14.0 + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/kernel/third_party/GSL-5.0.0/tests/algorithm_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/algorithm_tests.cpp new file mode 100644 index 0000000..b4a4f81 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/algorithm_tests.cpp @@ -0,0 +1,224 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include // for array +#include // for size_t +#include // for copy +#include // for span +#include + +#include "deathTestCommon.h" + +namespace gsl +{ +struct fail_fast; +} // namespace gsl + +using namespace gsl; + +TEST(algorithm_tests, same_type) +{ + // dynamic source and destination span + { + std::array src{1, 2, 3, 4, 5}; + std::array dst{}; + + const span src_span(src); + const span dst_span(dst); + + copy(src_span, dst_span); + copy(src_span, dst_span.subspan(src_span.size())); + + for (std::size_t i = 0; i < src.size(); ++i) + { + EXPECT_TRUE(dst[i] == src[i]); + EXPECT_TRUE(dst[i + src.size()] == src[i]); + } + } + + // static source and dynamic destination span + { + std::array src{1, 2, 3, 4, 5}; + std::array dst{}; + + const span src_span(src); + const span dst_span(dst); + + copy(src_span, dst_span); + copy(src_span, dst_span.subspan(src_span.size())); + + for (std::size_t i = 0; i < src.size(); ++i) + { + EXPECT_TRUE(dst[i] == src[i]); + EXPECT_TRUE(dst[i + src.size()] == src[i]); + } + } + + // dynamic source and static destination span + { + std::array src{1, 2, 3, 4, 5}; + std::array dst{}; + + const gsl::span src_span(src); + const gsl::span dst_span(dst); + + copy(src_span, dst_span); + copy(src_span, dst_span.subspan(src_span.size())); + + for (std::size_t i = 0; i < src.size(); ++i) + { + EXPECT_TRUE(dst[i] == src[i]); + EXPECT_TRUE(dst[i + src.size()] == src[i]); + } + } + + // static source and destination span + { + std::array src{1, 2, 3, 4, 5}; + std::array dst{}; + + const span src_span(src); + const span dst_span(dst); + + copy(src_span, dst_span); + copy(src_span, dst_span.subspan(src_span.size())); + + for (std::size_t i = 0; i < src.size(); ++i) + { + EXPECT_TRUE(dst[i] == src[i]); + EXPECT_TRUE(dst[i + src.size()] == src[i]); + } + } +} + +TEST(algorithm_tests, compatible_type) +{ + // dynamic source and destination span + { + std::array src{1, 2, 3, 4, 5}; + std::array dst{}; + + const span src_span(src); + const span dst_span(dst); + + copy(src_span, dst_span); + copy(src_span, dst_span.subspan(src_span.size())); + + for (std::size_t i = 0; i < src.size(); ++i) + { + EXPECT_TRUE(dst[i] == src[i]); + EXPECT_TRUE(dst[i + src.size()] == src[i]); + } + } + + // static source and dynamic destination span + { + std::array src{1, 2, 3, 4, 5}; + std::array dst{}; + + const span src_span(src); + const span dst_span(dst); + + copy(src_span, dst_span); + copy(src_span, dst_span.subspan(src_span.size())); + + for (std::size_t i = 0; i < src.size(); ++i) + { + EXPECT_TRUE(dst[i] == src[i]); + EXPECT_TRUE(dst[i + src.size()] == src[i]); + } + } + + // dynamic source and static destination span + { + std::array src{1, 2, 3, 4, 5}; + std::array dst{}; + + const span src_span(src); + const span dst_span(dst); + + copy(src_span, dst_span); + copy(src_span, dst_span.subspan(src_span.size())); + + for (std::size_t i = 0; i < src.size(); ++i) + { + EXPECT_TRUE(dst[i] == src[i]); + EXPECT_TRUE(dst[i + src.size()] == src[i]); + } + } + + // static source and destination span + { + std::array src{1, 2, 3, 4, 5}; + std::array dst{}; + + const span src_span(src); + const span dst_span(dst); + + copy(src_span, dst_span); + copy(src_span, dst_span.subspan(src_span.size())); + + for (std::size_t i = 0; i < src.size(); ++i) + { + EXPECT_TRUE(dst[i] == src[i]); + EXPECT_TRUE(dst[i + src.size()] == src[i]); + } + } +} + +#ifdef CONFIRM_COMPILATION_ERRORS +TEST(algorithm_tests, incompatible_type) +{ + std::array src{1, 2, 3, 4}; + std::array dst{}; + + span src_span_dyn(src); + span src_span_static(src); + span dst_span_dyn(dst); + span dst_span_static(gsl::make_span(dst)); + + // every line should produce a compilation error + copy(src_span_dyn, dst_span_dyn); + copy(src_span_dyn, dst_span_static); + copy(src_span_static, dst_span_dyn); + copy(src_span_static, dst_span_static); +} +#endif + +TEST(algorithm_tests, small_destination_span) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. small_destination_span"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + std::array src{1, 2, 3, 4}; + std::array dst{}; + + const span src_span_dyn(src); + const span src_span_static(src); + const span dst_span_dyn(dst); + const span dst_span_static(dst); + + EXPECT_DEATH(copy(src_span_dyn, dst_span_dyn), expected); + EXPECT_DEATH(copy(src_span_dyn, dst_span_static), expected); + EXPECT_DEATH(copy(src_span_static, dst_span_dyn), expected); + +#ifdef CONFIRM_COMPILATION_ERRORS + copy(src_span_static, dst_span_static); +#endif +} diff --git a/kernel/third_party/GSL-5.0.0/tests/assertion_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/assertion_tests.cpp new file mode 100644 index 0000000..231befc --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/assertion_tests.cpp @@ -0,0 +1,60 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "deathTestCommon.h" +#include // for Ensures, Expects +#include + +using namespace gsl; + +namespace +{ + +int f(int i) +{ + Expects(i > 0 && i < 10); + return i; +} + +int g(int i) +{ + i++; + Ensures(i > 0 && i < 10); + return i; +} +} // namespace + +TEST(assertion_tests, expects) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. expects"; + std::abort(); + }); + + EXPECT_TRUE(f(2) == 2); + EXPECT_DEATH(f(10), GetExpectedDeathString(terminateHandler)); +} + +TEST(assertion_tests, ensures) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. ensures"; + std::abort(); + }); + + EXPECT_TRUE(g(2) == 3); + EXPECT_DEATH(g(9), GetExpectedDeathString(terminateHandler)); +} diff --git a/kernel/third_party/GSL-5.0.0/tests/at_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/at_tests.cpp new file mode 100644 index 0000000..a280907 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/at_tests.cpp @@ -0,0 +1,173 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include + +#include // for at + +#include // for array +#include // for size_t +#include // for terminate +#include // for initializer_list +#include // for vector +#if defined(__cplusplus) && __cplusplus >= 202002L +#include +#endif // __cplusplus >= 202002L + +#include "deathTestCommon.h" + +TEST(at_tests, static_array) +{ + int a[4] = {1, 2, 3, 4}; + const int (&c_a)[4] = a; + + for (int i = 0; i < 4; ++i) + { + EXPECT_TRUE(&gsl::at(a, i) == &a[i]); + EXPECT_TRUE(&gsl::at(c_a, i) == &a[i]); + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. static_array"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + EXPECT_DEATH(gsl::at(a, -1), expected); + EXPECT_DEATH(gsl::at(a, 4), expected); + EXPECT_DEATH(gsl::at(c_a, -1), expected); + EXPECT_DEATH(gsl::at(c_a, 4), expected); +} + +TEST(at_tests, std_array) +{ + std::array a = {1, 2, 3, 4}; + const std::array& c_a = a; + + for (int i = 0; i < 4; ++i) + { + EXPECT_TRUE(&gsl::at(a, i) == &a[static_cast(i)]); + EXPECT_TRUE(&gsl::at(c_a, i) == &a[static_cast(i)]); + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. std_array"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + EXPECT_DEATH(gsl::at(a, -1), expected); + EXPECT_DEATH(gsl::at(a, 4), expected); + EXPECT_DEATH(gsl::at(c_a, -1), expected); + EXPECT_DEATH(gsl::at(c_a, 4), expected); +} + +TEST(at_tests, std_vector) +{ + std::vector a = {1, 2, 3, 4}; + const std::vector& c_a = a; + + for (int i = 0; i < 4; ++i) + { + EXPECT_TRUE(&gsl::at(a, i) == &a[static_cast(i)]); + EXPECT_TRUE(&gsl::at(c_a, i) == &a[static_cast(i)]); + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. std_vector"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + EXPECT_DEATH(gsl::at(a, -1), expected); + EXPECT_DEATH(gsl::at(a, 4), expected); + EXPECT_DEATH(gsl::at(c_a, -1), expected); + EXPECT_DEATH(gsl::at(c_a, 4), expected); +} + +TEST(at_tests, InitializerList) +{ + const std::initializer_list a = {1, 2, 3, 4}; + + for (int i = 0; i < 4; ++i) + { + EXPECT_TRUE(gsl::at(a, i) == i + 1); + EXPECT_TRUE(gsl::at({1, 2, 3, 4}, i) == i + 1); + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. InitializerList"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + EXPECT_DEATH(gsl::at(a, -1), expected); + EXPECT_DEATH(gsl::at(a, 4), expected); + EXPECT_DEATH(gsl::at({1, 2, 3, 4}, -1), expected); + EXPECT_DEATH(gsl::at({1, 2, 3, 4}, 4), expected); +} + +#if defined(FORCE_STD_SPAN_TESTS) || defined(__cpp_lib_span) && __cpp_lib_span >= 202002L +TEST(at_tests, std_span) +{ + std::vector vec{1, 2, 3, 4, 5}; + std::span sp{vec}; + + std::vector cvec{1, 2, 3, 4, 5}; + std::span csp{cvec}; + + for (gsl::index i = 0; i < gsl::narrow_cast(vec.size()); ++i) + { + EXPECT_TRUE(&gsl::at(sp, i) == &vec[gsl::narrow_cast(i)]); + EXPECT_TRUE(&gsl::at(csp, i) == &cvec[gsl::narrow_cast(i)]); + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. std_span"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + EXPECT_DEATH(gsl::at(sp, -1), expected); + EXPECT_DEATH(gsl::at(sp, gsl::narrow_cast(sp.size())), expected); + EXPECT_DEATH(gsl::at(csp, -1), expected); + EXPECT_DEATH(gsl::at(csp, gsl::narrow_cast(sp.size())), expected); +} +#endif // defined(FORCE_STD_SPAN_TESTS) || defined(__cpp_lib_span) && __cpp_lib_span >= 202002L + +#if !defined(_MSC_VER) || defined(__clang__) || _MSC_VER >= 1910 +static constexpr bool test_constexpr() +{ + int a1[4] = {1, 2, 3, 4}; + const int (&c_a1)[4] = a1; + std::array a2 = {1, 2, 3, 4}; + const std::array& c_a2 = a2; + + for (int i = 0; i < 4; ++i) + { + if (&gsl::at(a1, i) != &a1[i]) return false; + if (&gsl::at(c_a1, i) != &a1[i]) return false; + // requires C++17: + // if (&gsl::at(a2, i) != &a2[static_cast(i)]) return false; + if (&gsl::at(c_a2, i) != &c_a2[static_cast(i)]) return false; + if (gsl::at({1, 2, 3, 4}, i) != i + 1) return false; + } + + return true; +} + +static_assert(test_constexpr(), "FAIL"); +#endif diff --git a/kernel/third_party/GSL-5.0.0/tests/byte_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/byte_tests.cpp new file mode 100644 index 0000000..e361e8a --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/byte_tests.cpp @@ -0,0 +1,178 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include + +#define GSL_USE_STD_BYTE 0 +#include // for to_byte, to_integer, byte, operator&, ope... + +#include +#include + +using namespace std; +using namespace gsl; + +namespace +{ +int modify_both(gsl::byte& b, int& i) +{ + i = 10; + b = to_byte<5>(); + return i; +} + +TEST(byte_tests, construction) +{ + { + const gsl::byte b = static_cast(4); + EXPECT_TRUE(static_cast(b) == 4); + } + + { + const gsl::byte b = gsl::byte(12); + EXPECT_TRUE(static_cast(b) == 12); + } + + { + const gsl::byte b = to_byte<12>(); + EXPECT_TRUE(static_cast(b) == 12); + } + { + const unsigned char uc = 12; + const gsl::byte b = to_byte(uc); + EXPECT_TRUE(static_cast(b) == 12); + } + +#if defined(__cplusplus) && (__cplusplus >= 201703L) + { + const gsl::byte b{14}; + EXPECT_TRUE(static_cast(b) == 14); + } +#endif + +#ifdef CONFIRM_COMPILATION_ERRORS + to_byte(char{}); + to_byte(3); + to_byte(3u); + to_byte<-1>(); + to_byte<256u>(); +#endif +} + +TEST(byte_tests, bitwise_operations) +{ + const gsl::byte b = to_byte<0xFF>(); + + gsl::byte a = to_byte<0x00>(); + EXPECT_TRUE((b | a) == to_byte<0xFF>()); + EXPECT_TRUE(a == to_byte<0x00>()); + + a |= b; + EXPECT_TRUE(a == to_byte<0xFF>()); + + a = to_byte<0x01>(); + EXPECT_TRUE((b & a) == to_byte<0x01>()); + + a &= b; + EXPECT_TRUE(a == to_byte<0x01>()); + + EXPECT_TRUE((b ^ a) == to_byte<0xFE>()); + + EXPECT_TRUE(a == to_byte<0x01>()); + a ^= b; + EXPECT_TRUE(a == to_byte<0xFE>()); + + a = to_byte<0x01>(); + EXPECT_TRUE(~a == to_byte<0xFE>()); + + a = to_byte<0xFF>(); + EXPECT_TRUE((a << 4) == to_byte<0xF0>()); + EXPECT_TRUE((a >> 4) == to_byte<0x0F>()); + + a <<= 4; + EXPECT_TRUE(a == to_byte<0xF0>()); + a >>= 4; + EXPECT_TRUE(a == to_byte<0x0F>()); +} + +TEST(byte_tests, to_integer) +{ + const gsl::byte b = to_byte<0x12>(); + + EXPECT_TRUE(0x12 == gsl::to_integer(b)); + EXPECT_TRUE(0x12 == gsl::to_integer(b)); + EXPECT_TRUE(0x12 == gsl::to_integer(b)); + EXPECT_TRUE(0x12 == gsl::to_integer(b)); + + EXPECT_TRUE(0x12 == gsl::to_integer(b)); + EXPECT_TRUE(0x12 == gsl::to_integer(b)); + EXPECT_TRUE(0x12 == gsl::to_integer(b)); + EXPECT_TRUE(0x12 == gsl::to_integer(b)); + + // EXPECT_TRUE(0x12 == gsl::to_integer(b)); // expect compile-time error + // EXPECT_TRUE(0x12 == gsl::to_integer(b)); // expect compile-time error +} + +TEST(byte_tests, aliasing) +{ + int i{0}; + const int res = modify_both(reinterpret_cast(i), i); + EXPECT_TRUE(res == i); +} + +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + +template +static constexpr bool LShiftCompilesFor = false; +template +static constexpr bool LShiftCompilesFor< + U, void_t(declval(), declval()))>> = true; +static_assert(!LShiftCompilesFor, "!LShiftCompilesFor"); + +template +static constexpr bool RShiftCompilesFor = false; +template +static constexpr bool RShiftCompilesFor< + U, void_t> (declval(), declval()))>> = true; +static_assert(!RShiftCompilesFor, "!RShiftCompilesFor"); + +template +static constexpr bool LShiftAssignCompilesFor = false; +template +static constexpr bool LShiftAssignCompilesFor< + U, void_t(declval(), declval()))>> = true; +static_assert(!LShiftAssignCompilesFor, "!LShiftAssignCompilesFor"); + +template +static constexpr bool RShiftAssignCompilesFor = false; +template +static constexpr bool RShiftAssignCompilesFor< + U, void_t>= (declval(), declval()))>> = true; +static_assert(!RShiftAssignCompilesFor, "!RShiftAssignCompilesFor"); + +template +static constexpr bool ToIntegerCompilesFor = false; +template +static constexpr bool ToIntegerCompilesFor(gsl::byte{}))>> = + true; +static_assert(!ToIntegerCompilesFor, "!ToIntegerCompilesFor"); + +} // namespace diff --git a/kernel/third_party/GSL-5.0.0/tests/constexpr_notnull_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/constexpr_notnull_tests.cpp new file mode 100644 index 0000000..f735b14 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/constexpr_notnull_tests.cpp @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2025 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include // for not_null +#include + +#include // for declval + +using namespace gsl; + +namespace +{ +constexpr bool comparison_test(const int* ptr1, const int* ptr2) +{ + const not_null p1(ptr1); + const not_null p1_same(ptr1); + const not_null p2(ptr2); + + // Testing operator== + const bool eq_result = (p1 == p1_same); // Should be true + const bool neq_result = (p1 != p2); // Should be true + + // Testing operator<= and operator>= + const bool le_result = (p1 <= p1_same); // Should be true + const bool ge_result = (p1 >= p1_same); // Should be true + + // The exact comparison results will depend on pointer ordering, + // but we can verify that the basic equality checks work as expected + return eq_result && neq_result && le_result && ge_result; +} + +constexpr bool workaround_test(const int* ptr1, const int* ptr2) +{ + const not_null p1(ptr1); + const not_null p1_same(ptr1); + const not_null p2(ptr2); + + // Using .get() to compare + const bool eq_result = (p1.get() == p1_same.get()); // Should be true + const bool neq_result = (p1.get() != p2.get()); // Should be true + + return eq_result && neq_result; +} +} // namespace + +constexpr int test_value1 = 1; +constexpr int test_value2 = 2; + +static_assert(comparison_test(&test_value1, &test_value2), + "not_null comparison operators should be constexpr"); +static_assert(workaround_test(&test_value1, &test_value2), + "not_null .get() comparison workaround should work"); + +TEST(notnull_constexpr_tests, TestNotNullConstexprComparison) +{ + // This test simply verifies that the constexpr functions compile and run + // If we got here, it means the constexpr comparison operators are working + static const int value1 = 1; + static const int value2 = 2; + EXPECT_TRUE(comparison_test(&value1, &value2)); + EXPECT_TRUE(workaround_test(&value1, &value2)); +} diff --git a/kernel/third_party/GSL-5.0.0/tests/deathTestCommon.h b/kernel/third_party/GSL-5.0.0/tests/deathTestCommon.h new file mode 100644 index 0000000..e92beb3 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/deathTestCommon.h @@ -0,0 +1,11 @@ +#pragma once +#include + +constexpr char deathstring[] = "Expected Death"; +constexpr char failed_set_terminate_deathstring[] = ".*"; + +// This prevents a failed call to set_terminate from failing the test suite. +constexpr const char* GetExpectedDeathString(std::terminate_handler handle) +{ + return handle ? deathstring : failed_set_terminate_deathstring; +} diff --git a/kernel/third_party/GSL-5.0.0/tests/dyn_array_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/dyn_array_tests.cpp new file mode 100644 index 0000000..46efc63 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/dyn_array_tests.cpp @@ -0,0 +1,708 @@ +#include + +#include "deathTestCommon.h" +#include "gsl/dyn_array" +#include +#include +#include +#include +#include +#include +#include + +// Despite using and utilities in this test, they +// are not being included directly by this file as a test to ensure +// transitive inclusion via . + +static_assert(sizeof(gsl::dyn_array) == 2 * sizeof(void*), + "gsl::dyn_array (with the default allocator) should be 16 bytes"); +static_assert( + std::is_convertible::iterator, gsl::dyn_array::const_iterator>::value, + "gsl::dyn_array iterator should be implicitly convertible to const_iterator"); +static_assert(!std::is_constructible::iterator, gsl::dyn_array&>::value, + "dyn_array::iterator should not be constructible from dyn_array"); +static_assert( + !std::is_constructible::iterator, int*, std::size_t, std::size_t>::value, + "dyn_array::iterator should not be constructible from an arbitrary state triple"); +static_assert( + !std::is_constructible::const_iterator, const gsl::dyn_array&>::value, + "dyn_array::const_iterator should not be constructible from dyn_array"); +static_assert(!std::is_constructible::const_iterator, const int*, std::size_t, + std::size_t>::value, + "dyn_array::const_iterator should not be constructible from an arbitrary state " + "triple"); +static_assert(std::is_copy_constructible::iterator>::value, + "dyn_array::iterator should remain copy constructible"); + +#if defined(__cpp_lib_concepts) && (__cpp_lib_concepts >= 202002L) +static_assert(std::input_iterator::iterator>, + "gsl::dyn_array should expose a valid input_iterator"); +#endif /* __cpp_lib_concepts >= 202002L */ + +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) +static_assert(std::ranges::input_range>, + "gsl::dyn_array should be a valid input range"); +#endif /* __cpp_lib_ranges >= 201911L */ + +TEST(dyn_array_tests, default_ctor) +{ + gsl::dyn_array diamondbacks; + EXPECT_TRUE(diamondbacks.empty()); + EXPECT_EQ(diamondbacks.size(), 0); + EXPECT_EQ(diamondbacks.data(), nullptr); +} + +TEST(dyn_array_tests, count_ctor) +{ + gsl::dyn_array athletics(10); + EXPECT_FALSE(athletics.empty()); + EXPECT_EQ(athletics.size(), 10); + EXPECT_NE(athletics.data(), nullptr); + + gsl::dyn_array braves(0); + EXPECT_TRUE(braves.empty()); + EXPECT_EQ(braves.size(), 0); + EXPECT_EQ(braves.data(), nullptr); + EXPECT_TRUE(std::all_of(braves.begin(), braves.end(), [](char c) { return c == char{}; })); +} + +TEST(dyn_array_tests, count_value_ctor) +{ + gsl::dyn_array orioles(10, 'c'); + EXPECT_FALSE(orioles.empty()); + EXPECT_EQ(orioles.size(), 10); + EXPECT_NE(orioles.data(), nullptr); + EXPECT_TRUE(std::all_of(orioles.begin(), orioles.end(), [](char c) { return c == 'c'; })); + + gsl::dyn_array redsox(10, 42); + EXPECT_FALSE(redsox.empty()); + EXPECT_EQ(redsox.size(), 10); + EXPECT_NE(redsox.data(), nullptr); + EXPECT_TRUE(std::all_of(redsox.begin(), redsox.end(), [](int i) { return i == 42; })); +} + +TEST(dyn_array_tests, inputit_ctor) +{ + std::vector cubs(10, 'c'); + gsl::dyn_array whitesox(cubs.begin(), cubs.end()); + EXPECT_FALSE(whitesox.empty()); + EXPECT_EQ(whitesox.size(), cubs.size()); + EXPECT_NE(whitesox.data(), nullptr); + EXPECT_TRUE(std::all_of(whitesox.begin(), whitesox.end(), [](char c) { return c == 'c'; })); +} + +TEST(dyn_array_tests, copy_ctor) +{ + gsl::dyn_array reds(10, 'c'); + gsl::dyn_array guardians(reds); + EXPECT_FALSE(guardians.empty()); + EXPECT_EQ(guardians.size(), reds.size()); + EXPECT_NE(guardians.data(), nullptr); + EXPECT_TRUE(std::all_of(guardians.begin(), guardians.end(), [](char c) { return c == 'c'; })); +} + +TEST(dyn_array_tests, access_operator) +{ + gsl::dyn_array rockies(10, 'c'); + using ST = typename decltype(rockies)::size_type; + for (int i = 0; i < gsl::narrow(rockies.size()); i++) + EXPECT_EQ(rockies[gsl::narrow(i)], 'c'); + for (int i = 0; i < gsl::narrow(rockies.size()); i++) rockies[gsl::narrow(i)] = 'r'; + for (int i = 0; i < gsl::narrow(rockies.size()); i++) + EXPECT_EQ(rockies[gsl::narrow(i)], 'r'); + gsl::dyn_array tigers(10); + for (int i = 0; i < gsl::narrow(tigers.size()); i++) tigers[gsl::narrow(i)] = i; + for (int i = 0; i < gsl::narrow(tigers.size()); i++) + EXPECT_EQ(tigers[gsl::narrow(i)], i); +} + +TEST(dyn_array_tests, iterators) +{ + gsl::dyn_array astros(10, 'c'); + for (auto it = astros.begin(); it != astros.end(); it++) EXPECT_EQ(*it, 'c'); + for (auto it = astros.begin(); it != astros.end(); it++) *it = 'r'; + for (auto it = astros.begin(); it != astros.end(); it++) EXPECT_EQ(*it, 'r'); + EXPECT_TRUE(std::all_of(astros.begin(), astros.end(), [](char c) { return c == 'r'; })); + + gsl::dyn_array royals(10, 'c'); + for (auto it = royals.begin(); it != royals.end(); ++it) EXPECT_EQ(*it, 'c'); + for (auto it = royals.begin(); it != royals.end(); ++it) *it = 'r'; + for (auto it = royals.begin(); it != royals.end(); ++it) EXPECT_EQ(*it, 'r'); + EXPECT_TRUE(std::all_of(royals.begin(), royals.end(), [](char c) { return c == 'r'; })); +} + +TEST(dyn_array_tests, range_for) +{ + gsl::dyn_array angels(10, 'c'); + for (auto x : angels) EXPECT_EQ(x, 'c'); + for (auto& x : angels) x = 'r'; + for (auto x : angels) EXPECT_EQ(x, 'r'); + EXPECT_TRUE(std::all_of(angels.begin(), angels.end(), [](char c) { return c == 'r'; })); +} + +TEST(dyn_array_tests, use_std_algorithms) +{ + gsl::dyn_array dodgers(26); + std::generate(dodgers.begin(), dodgers.end(), [i = 0]() mutable { return 'a' + i++; }); + char ch = 'a'; + for (auto x : dodgers) EXPECT_EQ(x, ch++); + EXPECT_EQ(std::find(dodgers.begin(), dodgers.end(), 'a'), dodgers.begin()); + { + auto it = std::find(dodgers.begin(), dodgers.end(), 'c'); + EXPECT_EQ(std::distance(dodgers.begin(), it), 'c' - 'a'); + EXPECT_EQ(std::distance(it, dodgers.begin()), 'a' - 'c'); + } + { + auto it = std::lower_bound(dodgers.begin(), dodgers.end(), 'j'); + EXPECT_EQ(*it, 'j'); + EXPECT_EQ(std::distance(dodgers.begin(), it), 'j' - 'a'); + EXPECT_EQ(std::distance(it, dodgers.begin()), 'a' - 'j'); + } + EXPECT_EQ(dodgers.begin(), std::begin(dodgers)); + EXPECT_EQ(dodgers.end(), std::end(dodgers)); +} + +#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L) +constexpr auto default_constructed_count_dyn_array_is_constexpr() +{ + gsl::dyn_array values(3); + return values.size() == 3 && values[0] == 0 && values[1] == 0 && values[2] == 0; +} + +TEST(dyn_array_tests, constexprness) +{ + constexpr gsl::dyn_array marlins; + static_assert(marlins == marlins); + static_assert(marlins.empty()); + static_assert(marlins.size() == 0); + static_assert(marlins.data() == nullptr); + static_assert(marlins.begin() == marlins.end()); + static_assert(std::distance(marlins.begin(), marlins.end()) == 0); + static_assert(default_constructed_count_dyn_array_is_constexpr()); +} +#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ + +#if defined(__cpp_lib_ranges) && (__cpp_lib_ranges >= 201911L) +TEST(dyn_array_tests, ranges) +{ + gsl::dyn_array brewers(26); + std::ranges::generate(brewers, [c = 'a']() mutable { return c++; }); + char ch = 'a'; + for (auto x : brewers) EXPECT_EQ(x, ch++); + EXPECT_EQ(std::ranges::find(brewers, 'a'), std::ranges::begin(brewers)); + { + auto it = std::ranges::find(brewers, 'c'); + EXPECT_EQ(std::ranges::distance(std::ranges::begin(brewers), it), 'c' - 'a'); + EXPECT_EQ(std::ranges::distance(it, std::ranges::begin(brewers)), 'a' - 'c'); + } + +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) + std::vector twins(10, 'c'); + gsl::dyn_array mets(std::from_range, twins); + EXPECT_EQ(twins.size(), mets.size()); + EXPECT_TRUE(std::ranges::all_of(mets, [](char c) { return c == 'c'; })); +#endif /* __cpp_lib_containers_ranges >= 202202L */ +} +#endif /* __cpp_lib_ranges >= 201911L */ + +#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L) +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif // defined(__clang__) +template +struct ConstexprAllocator +{ + using value_type = T; + + T buf[N]{}; + std::size_t sz{}; + + constexpr ConstexprAllocator() = default; + + template + constexpr ConstexprAllocator(const ConstexprAllocator&) noexcept : buf{}, sz{} + {} + + template + struct rebind + { + using other = ConstexprAllocator; + }; + + constexpr auto allocate(std::size_t n) -> value_type* + { + auto addr = &buf[sz]; + sz += n; + return addr; + } + + constexpr void deallocate(value_type*, std::size_t) noexcept {} +}; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // defined(__clang__) + +template +constexpr auto operator==(const ConstexprAllocator& lhs, + const ConstexprAllocator& rhs) noexcept +{ + return std::addressof(lhs) == std::addressof(rhs); +} + +template +constexpr auto operator!=(const ConstexprAllocator& lhs, + const ConstexprAllocator& rhs) noexcept +{ + return !(lhs == rhs); +} +#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ + +template +static int AllocCounter = 0; + +template +static int DeallocCounter = 0; + +struct LifetimeCounter +{ + static int alive_count; + + int value{}; + + explicit LifetimeCounter(int v = 0) : value(v) { ++alive_count; } + + LifetimeCounter(const LifetimeCounter& other) : value(other.value) { ++alive_count; } + + ~LifetimeCounter() { --alive_count; } +}; + +int LifetimeCounter::alive_count = 0; + +struct DefaultConstructionCounter +{ + static int default_constructor_count; + static int copy_constructor_count; + + int value{}; + + DefaultConstructionCounter() { ++default_constructor_count; } + + DefaultConstructionCounter(const DefaultConstructionCounter& other) : value(other.value) + { + ++copy_constructor_count; + } + + static void reset() + { + default_constructor_count = 0; + copy_constructor_count = 0; + } +}; + +int DefaultConstructionCounter::default_constructor_count = 0; +int DefaultConstructionCounter::copy_constructor_count = 0; + +struct DefaultOnlyElement +{ + DefaultOnlyElement() = default; + DefaultOnlyElement(const DefaultOnlyElement&) = delete; + DefaultOnlyElement& operator=(const DefaultOnlyElement&) = delete; + DefaultOnlyElement(DefaultOnlyElement&&) = delete; + DefaultOnlyElement& operator=(DefaultOnlyElement&&) = delete; +}; + +struct ThrowOnCopy +{ + static int alive_count; + static int copy_count; + static int throw_on_copy_index; + + int value{}; + + explicit ThrowOnCopy(int v = 0) : value(v) { ++alive_count; } + + ThrowOnCopy(const ThrowOnCopy& other) : value(other.value) + { + if (copy_count == throw_on_copy_index) + { + ++copy_count; + throw 42; + } + ++copy_count; + ++alive_count; + } + + ~ThrowOnCopy() { --alive_count; } +}; + +int ThrowOnCopy::alive_count = 0; +int ThrowOnCopy::copy_count = 0; +int ThrowOnCopy::throw_on_copy_index = -1; + +template +class Newocator +{ +public: + using value_type = T; + + Newocator() = default; + + template + Newocator(const Newocator&) noexcept + {} + + static void init() + { + AllocCounter> = 0; + DeallocCounter> = 0; + } + + static void check() { EXPECT_EQ(AllocCounter>, DeallocCounter>); } + + auto allocate(std::size_t n) -> value_type* + { + AllocCounter> ++; + return static_cast(::operator new(n * sizeof(value_type))); + } + + void deallocate(value_type* p, std::size_t) noexcept + { + DeallocCounter> ++; + ::operator delete(p); + } + + template + struct rebind + { + using other = Newocator; + }; +}; + +template +constexpr auto operator==(const Newocator&, const Newocator&) noexcept +{ + return true; +} + +template +constexpr auto operator!=(const Newocator& lhs, const Newocator& rhs) noexcept +{ + return !(lhs == rhs); +} + +template +class OwnershipTrackingAllocator +{ +public: + using value_type = T; + + OwnershipTrackingAllocator() noexcept : owner_id(next_owner_id()) { ++next_owner_id(); } + + explicit OwnershipTrackingAllocator(int owner) noexcept : owner_id(owner) {} + + template + OwnershipTrackingAllocator(const OwnershipTrackingAllocator& other) noexcept + : owner_id(other.owner()) + {} + + auto allocate(std::size_t count) -> value_type* + { + static_assert(alignof(value_type) <= alignof(int), + "test allocator only supports types with int-or-smaller alignment"); + auto raw = + static_cast(::operator new(sizeof(int) + count * sizeof(value_type))); + *reinterpret_cast(raw) = owner_id; + ++allocation_count(); + return reinterpret_cast(raw + sizeof(int)); + } + + void deallocate(value_type* pointer, std::size_t) noexcept + { + auto raw = reinterpret_cast(pointer) - sizeof(int); + if (*reinterpret_cast(raw) != owner_id) { ++mismatched_deallocation_count(); } + ++deallocation_count(); + ::operator delete(raw); + } + + auto owner() const noexcept { return owner_id; } + + static void reset() + { + next_owner_id() = 1; + allocation_count() = 0; + deallocation_count() = 0; + mismatched_deallocation_count() = 0; + } + + static auto mismatched_deallocations() { return mismatched_deallocation_count(); } + +private: + int owner_id; + + static auto next_owner_id() -> int& + { + static int value = 1; + return value; + } + + static auto allocation_count() -> int& + { + static int value = 0; + return value; + } + + static auto deallocation_count() -> int& + { + static int value = 0; + return value; + } + + static auto mismatched_deallocation_count() -> int& + { + static int value = 0; + return value; + } +}; + +template +constexpr auto operator==(const OwnershipTrackingAllocator& lhs, + const OwnershipTrackingAllocator& rhs) noexcept +{ + return lhs.owner() == rhs.owner(); +} + +template +constexpr auto operator!=(const OwnershipTrackingAllocator& lhs, + const OwnershipTrackingAllocator& rhs) noexcept +{ + return !(lhs == rhs); +} + +TEST(dyn_array_tests, custom_allocator_models_allocator) +{ + using traits = std::allocator_traits>; + using ptr = traits::pointer; + + static_assert(std::is_same::value, "allocator trait type mismatch"); + static_assert(std::is_same::value, "allocator trait type mismatch"); + + Newocator alloc; + auto p = traits::allocate(alloc, 1); + traits::deallocate(alloc, p, 1); + +#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L) + using constexpr_traits = std::allocator_traits>; + static_assert(std::is_same::value, + "allocator trait type mismatch"); +#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ +} + +TEST(dyn_array_tests, custom_allocator) +{ +#if defined(__cpp_lib_constexpr_dynamic_alloc) && (__cpp_lib_constexpr_dynamic_alloc >= 201907L) + static constexpr gsl::dyn_array> mets(10, 'c'); + static_assert(mets.size() == 10); + static_assert(mets[0] == 'c'); + static_assert(std::all_of(std::begin(mets), std::end(mets), [](char c) { return c == 'c'; })); +#endif /* __cpp_lib_constexpr_dynamic_alloc >= 201907L */ + + Newocator::init(); + { + gsl::dyn_array> yankees(10, 'c'); + EXPECT_EQ(yankees.size(), 10); + EXPECT_TRUE( + std::all_of(std::begin(yankees), std::end(yankees), [](char c) { return c == 'c'; })); + yankees[0] = 'a'; + yankees[1] = 'b'; + EXPECT_EQ(yankees[0], 'a'); + EXPECT_EQ(yankees[1], 'b'); + EXPECT_EQ(yankees[2], 'c'); + yankees.get_allocator().deallocate(yankees.get_allocator().allocate(1), 1); + } + Newocator::check(); +} + +TEST(dyn_array_tests, non_trivial_elements_are_destroyed) +{ + LifetimeCounter::alive_count = 0; + + { + gsl::dyn_array values(5, LifetimeCounter{7}); + EXPECT_EQ(values.size(), 5); + EXPECT_EQ(LifetimeCounter::alive_count, 5); + } + + EXPECT_EQ(LifetimeCounter::alive_count, 0); +} + +TEST(dyn_array_tests, count_constructor_default_constructs_each_element) +{ + DefaultConstructionCounter::reset(); + + { + gsl::dyn_array values(4); + EXPECT_EQ(values.size(), 4); + } + + EXPECT_EQ(DefaultConstructionCounter::default_constructor_count, 4); + EXPECT_EQ(DefaultConstructionCounter::copy_constructor_count, 0); +} + +#ifdef GSL_DYN_ARRAY_COMPILE_FAILURE_TESTS +TEST(dyn_array_compile_failure_tests, count_constructor_accepts_default_constructible_only_elements) +{ + gsl::dyn_array values(4); + EXPECT_EQ(values.size(), 4); +} +#endif /* GSL_DYN_ARRAY_COMPILE_FAILURE_TESTS */ + +TEST(dyn_array_tests, failed_element_construction_rolls_back) +{ + ThrowOnCopy::alive_count = 0; + ThrowOnCopy::copy_count = 0; + ThrowOnCopy::throw_on_copy_index = 2; + + EXPECT_THROW((gsl::dyn_array(5, ThrowOnCopy{1})), int); + EXPECT_EQ(ThrowOnCopy::alive_count, 0); + + ThrowOnCopy::throw_on_copy_index = -1; +} + +TEST(dyn_array_tests, init_list) +{ + gsl::dyn_array phillies = {'a', 'b', 'c'}; + EXPECT_EQ(phillies.size(), 3); + EXPECT_EQ(phillies[0], 'a'); + EXPECT_EQ(phillies[1], 'b'); + EXPECT_EQ(phillies[2], 'c'); +} + +TEST(dyn_array_tests, const_operations) +{ + const gsl::dyn_array pirates{'a', 'b', 'c', 'd'}; + EXPECT_EQ(pirates.size(), 4); + EXPECT_EQ(pirates[0], 'a'); +} + +TEST(dyn_array_tests, reverse_iterator) +{ + const gsl::dyn_array padres{'a', 'b', 'c'}; + auto it = std::rbegin(padres); + EXPECT_EQ(*it++, 'c'); + EXPECT_EQ(*it++, 'b'); + EXPECT_EQ(*it++, 'a'); +} + +TEST(dyn_array_tests, random_access_iterator_arithmetic) +{ + gsl::dyn_array bluejays{'a', 'b', 'c', 'd'}; + + auto first = bluejays.begin(); + auto third = first + 2; + + EXPECT_EQ(*third, 'c'); + EXPECT_EQ(third - first, 2); + EXPECT_EQ(*(third - 1), 'b'); + EXPECT_EQ(*std::prev(third), 'b'); +} + +TEST(dyn_array_tests, random_access_iterator_arithmetic_accepts_negative_offsets) +{ + gsl::dyn_array bluejays{'a', 'b', 'c', 'd'}; + + auto third = bluejays.begin() + 2; + char previous{}; + char next{}; + + EXPECT_NO_THROW(previous = *(third + -1)); + EXPECT_EQ(previous, 'b'); + + EXPECT_NO_THROW(next = *(third - -1)); + EXPECT_EQ(next, 'd'); +} + +TEST(dyn_array_tests, input_iterator_constructor) +{ + std::istringstream stream{"n a t s"}; + std::istream_iterator first{stream}; + const std::istream_iterator last{}; + + gsl::dyn_array nationals(first, last); + + ASSERT_EQ(nationals.size(), 4); + EXPECT_EQ(nationals[0], 'n'); + EXPECT_EQ(nationals[1], 'a'); + EXPECT_EQ(nationals[2], 't'); + EXPECT_EQ(nationals[3], 's'); +} + +TEST(dyn_array_tests, contract_violations) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. dyn_array_contract_violations"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + gsl::dyn_array values(3, 'v'); + gsl::dyn_array other(3, 'o'); + + EXPECT_DEATH(values[values.size()], expected); + EXPECT_DEATH((void) *values.end(), expected); + EXPECT_DEATH(++values.end(), expected); + EXPECT_DEATH(--values.begin(), expected); + EXPECT_DEATH((void) (values.begin() == other.begin()), expected); +} + +#ifdef _MSC_VER +TEST(dyn_array_tests, unchecked_iterators) +{ + gsl::dyn_array values; + const gsl::dyn_array const_values(3, 'v'); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + + std::size_t count = 0; + for (const auto value : const_values) + { + EXPECT_EQ(value, 'v'); + ++count; + } + EXPECT_EQ(count, const_values.size()); + + EXPECT_EQ(values._Unchecked_begin(), nullptr); + EXPECT_EQ(values._Unchecked_end(), nullptr); +} +#endif /* _MSC_VER */ + +TEST(DynArrayTests, TypeConsistency) +{ + static_assert(std::is_same::value_type, int>::value, "Value type mismatch"); + static_assert(std::is_same::reference, int&>::value, + "Reference type mismatch"); + static_assert(std::is_same::const_reference, const int&>::value, + "Const reference type mismatch"); + static_assert(std::is_same::iterator::value_type, int>::value, + "Iterator value type mismatch"); + static_assert(std::is_same::iterator::reference, int&>::value, + "Iterator reference type mismatch"); + static_assert(std::is_same::iterator::const_reference, const int&>::value, + "Iterator const reference type mismatch"); + static_assert(std::is_same::size_type, std::size_t>::value, + "Size type mismatch"); + static_assert(std::is_same::difference_type, std::ptrdiff_t>::value, + "Difference type mismatch"); +} + +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) +TEST(dyn_array_tests, deduction_guides) +{ + std::vector giants{10}; +#if defined(__cpp_lib_containers_ranges) && (__cpp_lib_containers_ranges >= 202202L) + gsl::dyn_array mariners(std::from_range, giants); +#endif /* __cpp_lib_containers_ranges >= 202202L */ + gsl::dyn_array cardinals(std::begin(giants), std::end(giants)); +} +#endif /* __cpp_deduction_guides >= 201703L */ diff --git a/kernel/third_party/GSL-5.0.0/tests/no_exception_ensure_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/no_exception_ensure_tests.cpp new file mode 100644 index 0000000..fca88f2 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/no_exception_ensure_tests.cpp @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include // for std::exit +#include // for span +#include + +int operator_subscript_no_throw() noexcept +{ + int arr[10]; + const gsl::span sp{arr}; + return sp[11]; +} + +[[noreturn]] void test_terminate() { std::exit(0); } + +void setup_termination_handler() noexcept +{ +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + + auto& handler = gsl::details::get_terminate_handler(); + handler = &test_terminate; + +#else + + std::set_terminate(test_terminate); + +#endif +} + +int main() noexcept +{ + std::cout << "Running main() from " __FILE__ "\n"; + setup_termination_handler(); + operator_subscript_no_throw(); + return -1; +} diff --git a/kernel/third_party/GSL-5.0.0/tests/notnull_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/notnull_tests.cpp new file mode 100644 index 0000000..178433f --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/notnull_tests.cpp @@ -0,0 +1,736 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include + +#include // for not_null, operator<, operator<=, operator> + +#include // for addressof +#include // for uint16_t +#include // for shared_ptr, make_shared, operator<, opera... +#include // for operator<<, ostringstream, basic_ostream:... +#include // for basic_string, operator==, string, operator<< +#include // for declval +#include // for type_info +#include // for variant, monostate, get + +#include "deathTestCommon.h" +using namespace gsl; + +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + +struct MyBase +{ +}; +struct MyDerived : public MyBase +{ +}; +struct Unrelated +{ +}; + +// stand-in for a user-defined ref-counted class +template +struct RefCounted +{ + RefCounted(T* p) : p_(p) {} + operator T*() { return p_; } + T* p_; +}; + +// user defined smart pointer with comparison operators returning non bool value +template +struct CustomPtr +{ + CustomPtr(T* p) : p_(p) {} + operator T*() const { return p_; } + bool operator!=(std::nullptr_t) const { return p_ != nullptr; } + T* p_ = nullptr; +}; + +template +std::string operator==(CustomPtr const& lhs, CustomPtr const& rhs) +{ + GSL_SUPPRESS(type.1) + return reinterpret_cast(lhs.p_) == reinterpret_cast(rhs.p_) ? "true" + : "false"; +} + +template +std::string operator!=(CustomPtr const& lhs, CustomPtr const& rhs) +{ + GSL_SUPPRESS(type.1) + return reinterpret_cast(lhs.p_) != reinterpret_cast(rhs.p_) ? "true" + : "false"; +} + +template +std::string operator<(CustomPtr const& lhs, CustomPtr const& rhs) +{ + GSL_SUPPRESS(type.1) + return reinterpret_cast(lhs.p_) < reinterpret_cast(rhs.p_) ? "true" + : "false"; +} + +template +std::string operator>(CustomPtr const& lhs, CustomPtr const& rhs) +{ + GSL_SUPPRESS(type.1) + return reinterpret_cast(lhs.p_) > reinterpret_cast(rhs.p_) ? "true" + : "false"; +} + +template +std::string operator<=(CustomPtr const& lhs, CustomPtr const& rhs) +{ + GSL_SUPPRESS(type.1) + return reinterpret_cast(lhs.p_) <= reinterpret_cast(rhs.p_) ? "true" + : "false"; +} + +template +std::string operator>=(CustomPtr const& lhs, CustomPtr const& rhs) +{ + GSL_SUPPRESS(type.1) + return reinterpret_cast(lhs.p_) >= reinterpret_cast(rhs.p_) ? "true" + : "false"; +} + +struct NonCopyableNonMovable +{ + NonCopyableNonMovable() = default; + NonCopyableNonMovable(const NonCopyableNonMovable&) = delete; + NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete; + NonCopyableNonMovable(NonCopyableNonMovable&&) = delete; + NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete; +}; + +namespace +{ +GSL_SUPPRESS(f.4) +bool helper(not_null p) { return *p == 12; } +GSL_SUPPRESS(f.4) +bool helper_const(not_null p) { return *p == 12; } + +int* return_pointer() { return nullptr; } +} // namespace + +template +static constexpr bool CtorCompilesFor_A = false; +template +static constexpr bool + CtorCompilesFor_A{std::declval()})>> = true; + +template +static constexpr bool CtorCompilesFor_B = false; +template +static constexpr bool CtorCompilesFor_B{N})>> = true; + +template +static constexpr bool DefaultCtorCompilesFor = false; +template +static constexpr bool DefaultCtorCompilesFor{})>> = true; + +template +static constexpr bool CtorCompilesFor_C = false; +template +static constexpr bool + CtorCompilesFor_C{std::declval>()})>> = + true; + +TEST(notnull_tests, TestNotNullConstructors) +{ + { + static_assert(CtorCompilesFor_A, "CtorCompilesFor_A"); + static_assert(!CtorCompilesFor_A, "!CtorCompilesFor_A"); + static_assert(!CtorCompilesFor_B, "!CtorCompilesFor_B"); + static_assert(!DefaultCtorCompilesFor, "!DefaultCtorCompilesFor"); + static_assert(!CtorCompilesFor_C, "CtorCompilesFor_C"); + +#ifdef CONFIRM_COMPILATION_ERRORS + // Forbid non-nullptr assignable types + not_null> f(std::vector{1}); + not_null z(10); + not_null> y({1, 2}); +#endif + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. TestNotNullConstructors"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + { + // from shared pointer + int i = 12; + auto rp = RefCounted(&i); + not_null p(rp); + EXPECT_TRUE(p.get() == &i); + + not_null> x( + std::make_shared(10)); // shared_ptr is nullptr assignable + + int* pi = nullptr; + EXPECT_DEATH((not_null(pi)), expected); + } + + { + // from unique pointer + not_null> x( + std::make_unique(10)); // unique_ptr is nullptr assignable + + EXPECT_DEATH((not_null>(std::unique_ptr{})), expected); + } + + { + // from pointer to local + int t = 42; + + not_null x = &t; + helper(&t); + helper_const(&t); + + EXPECT_TRUE(*x == 42); + } + + { + // from raw pointer + // from not_null pointer + + int t = 42; + int* p = &t; + + not_null x = p; + helper(p); + helper_const(p); + helper(x); + helper_const(x); + + EXPECT_TRUE(*x == 42); + } + + { + // from raw const pointer + // from not_null const pointer + + int t = 42; + const int* cp = &t; + + not_null x = cp; + helper_const(cp); + helper_const(x); + + EXPECT_TRUE(*x == 42); + } + + { + // from not_null const pointer, using auto + int t = 42; + const int* cp = &t; + + auto x = not_null{cp}; + + EXPECT_TRUE(*x == 42); + } + + { + // from returned pointer + + EXPECT_DEATH(helper(return_pointer()), expected); + EXPECT_DEATH(helper_const(return_pointer()), expected); + } +} + +template +void ostream_helper(T v) +{ + not_null p(&v); + { + std::ostringstream os; + std::ostringstream ref; + os << static_cast(p); + ref << static_cast(&v); + EXPECT_TRUE(os.str() == ref.str()); + } + { + std::ostringstream os; + std::ostringstream ref; + os << *p; + ref << v; + EXPECT_TRUE(os.str() == ref.str()); + } +} + +TEST(notnull_tests, TestNotNullostream) +{ + ostream_helper(17); + ostream_helper(21.5f); + ostream_helper(3.4566e-7); + ostream_helper('c'); + ostream_helper(0x0123u); + ostream_helper("cstring"); + ostream_helper("string"); +} + +template +static constexpr bool AssignmentCompilesFor = false; +template +static constexpr bool + AssignmentCompilesFor&>().operator=( + std::declval&>()))>> = true; + +template +static constexpr bool SCastCompilesFor = false; +template +static constexpr bool + SCastCompilesFor(std::declval&>()))>> = + true; + +template +static constexpr bool RCastCompilesFor = false; +template +static constexpr bool RCastCompilesFor< + U, V, void_t(std::declval&>()))>> = true; + +TEST(notnull_tests, TestNotNullCasting) +{ + MyBase base; + MyDerived derived; + Unrelated unrelated; + not_null u{&unrelated}; + (void) u; + not_null p{&derived}; + not_null q(&base); + q = p; // allowed with heterogeneous copy ctor + EXPECT_TRUE(q == p); + + static_assert(AssignmentCompilesFor, + "AssignmentCompilesFor"); + static_assert(!AssignmentCompilesFor, + "!AssignmentCompilesFor"); + static_assert(!AssignmentCompilesFor, + "!AssignmentCompilesFor"); + static_assert(!AssignmentCompilesFor, + "!AssignmentCompilesFor"); + + static_assert(SCastCompilesFor, "SCastCompilesFor"); + static_assert(SCastCompilesFor, "SCastCompilesFor"); + static_assert(!SCastCompilesFor, "!SCastCompilesFor"); + static_assert(!SCastCompilesFor, + "!SCastCompilesFor"); + static_assert(!RCastCompilesFor, + "!SCastCompilesFor"); + static_assert(!RCastCompilesFor, + "!SCastCompilesFor"); + + not_null t(reinterpret_cast(p.get())); + EXPECT_TRUE(reinterpret_cast(p.get()) == reinterpret_cast(t.get())); + + (void) static_cast(p); + (void) static_cast(p); +} + +TEST(notnull_tests, TestNotNullAssignment) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. TestNotNullAssignmentd"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + int i = 12; + not_null p(&i); + EXPECT_TRUE(helper(p)); + + int* q = nullptr; + EXPECT_DEATH(p = not_null(q), expected); +} + +TEST(notnull_tests, TestNotNullRawPointerComparison) +{ + int ints[2] = {42, 43}; + int* p1 = &ints[0]; + const int* p2 = &ints[1]; + + using NotNull1 = not_null; + using NotNull2 = not_null; + + EXPECT_TRUE((NotNull1(p1) == NotNull1(p1)) == true); + EXPECT_TRUE((NotNull1(p1) == NotNull2(p2)) == false); + + EXPECT_TRUE((NotNull1(p1) != NotNull1(p1)) == false); + EXPECT_TRUE((NotNull1(p1) != NotNull2(p2)) == true); + + EXPECT_TRUE((NotNull1(p1) < NotNull1(p1)) == false); + EXPECT_TRUE((NotNull1(p1) < NotNull2(p2)) == (p1 < p2)); + EXPECT_TRUE((NotNull2(p2) < NotNull1(p1)) == (p2 < p1)); + + EXPECT_TRUE((NotNull1(p1) > NotNull1(p1)) == false); + EXPECT_TRUE((NotNull1(p1) > NotNull2(p2)) == (p1 > p2)); + EXPECT_TRUE((NotNull2(p2) > NotNull1(p1)) == (p2 > p1)); + + EXPECT_TRUE((NotNull1(p1) <= NotNull1(p1)) == true); + EXPECT_TRUE((NotNull1(p1) <= NotNull2(p2)) == (p1 <= p2)); + EXPECT_TRUE((NotNull2(p2) <= NotNull1(p1)) == (p2 <= p1)); +} + +TEST(notnull_tests, TestNotNullDereferenceOperator) +{ + { + auto sp1 = std::make_shared(); + + using NotNullSp1 = not_null; + EXPECT_TRUE(typeid(*sp1) == typeid(*NotNullSp1(sp1))); + EXPECT_TRUE(std::addressof(*NotNullSp1(sp1)) == std::addressof(*sp1)); + } + + { + int ints[1] = {42}; + CustomPtr p1(&ints[0]); + + using NotNull1 = not_null; + EXPECT_TRUE(typeid(*NotNull1(p1)) == typeid(*p1)); + EXPECT_TRUE(*NotNull1(p1) == 42); + *NotNull1(p1) = 43; + EXPECT_TRUE(ints[0] == 43); + } + + { + int v = 42; + gsl::not_null p(&v); + EXPECT_TRUE(typeid(*p) == typeid(*(&v))); + *p = 43; + EXPECT_TRUE(v == 43); + } +} + +TEST(notnull_tests, TestNotNullSharedPtrComparison) +{ + auto sp1 = std::make_shared(42); + auto sp2 = std::make_shared(43); + + using NotNullSp1 = not_null; + using NotNullSp2 = not_null; + + EXPECT_TRUE((NotNullSp1(sp1) == NotNullSp1(sp1)) == true); + EXPECT_TRUE((NotNullSp1(sp1) == NotNullSp2(sp2)) == false); + + EXPECT_TRUE((NotNullSp1(sp1) != NotNullSp1(sp1)) == false); + EXPECT_TRUE((NotNullSp1(sp1) != NotNullSp2(sp2)) == true); + + EXPECT_TRUE((NotNullSp1(sp1) < NotNullSp1(sp1)) == false); + EXPECT_TRUE((NotNullSp1(sp1) < NotNullSp2(sp2)) == (sp1 < sp2)); + EXPECT_TRUE((NotNullSp2(sp2) < NotNullSp1(sp1)) == (sp2 < sp1)); + + EXPECT_TRUE((NotNullSp1(sp1) > NotNullSp1(sp1)) == false); + EXPECT_TRUE((NotNullSp1(sp1) > NotNullSp2(sp2)) == (sp1 > sp2)); + EXPECT_TRUE((NotNullSp2(sp2) > NotNullSp1(sp1)) == (sp2 > sp1)); + + EXPECT_TRUE((NotNullSp1(sp1) <= NotNullSp1(sp1)) == true); + EXPECT_TRUE((NotNullSp1(sp1) <= NotNullSp2(sp2)) == (sp1 <= sp2)); + EXPECT_TRUE((NotNullSp2(sp2) <= NotNullSp1(sp1)) == (sp2 <= sp1)); + + EXPECT_TRUE((NotNullSp1(sp1) >= NotNullSp1(sp1)) == true); + EXPECT_TRUE((NotNullSp1(sp1) >= NotNullSp2(sp2)) == (sp1 >= sp2)); + EXPECT_TRUE((NotNullSp2(sp2) >= NotNullSp1(sp1)) == (sp2 >= sp1)); +} + +TEST(notnull_tests, TestNotNullCustomPtrComparison) +{ + int ints[2] = {42, 43}; + CustomPtr p1(&ints[0]); + CustomPtr p2(&ints[1]); + + using NotNull1 = not_null; + using NotNull2 = not_null; + + EXPECT_TRUE((NotNull1(p1) == NotNull1(p1)) == "true"); + EXPECT_TRUE((NotNull1(p1) == NotNull2(p2)) == "false"); + + EXPECT_TRUE((NotNull1(p1) != NotNull1(p1)) == "false"); + EXPECT_TRUE((NotNull1(p1) != NotNull2(p2)) == "true"); + + EXPECT_TRUE((NotNull1(p1) < NotNull1(p1)) == "false"); + EXPECT_TRUE((NotNull1(p1) < NotNull2(p2)) == (p1 < p2)); + EXPECT_TRUE((NotNull2(p2) < NotNull1(p1)) == (p2 < p1)); + + EXPECT_TRUE((NotNull1(p1) > NotNull1(p1)) == "false"); + EXPECT_TRUE((NotNull1(p1) > NotNull2(p2)) == (p1 > p2)); + EXPECT_TRUE((NotNull2(p2) > NotNull1(p1)) == (p2 > p1)); + + EXPECT_TRUE((NotNull1(p1) <= NotNull1(p1)) == "true"); + EXPECT_TRUE((NotNull1(p1) <= NotNull2(p2)) == (p1 <= p2)); + EXPECT_TRUE((NotNull2(p2) <= NotNull1(p1)) == (p2 <= p1)); + + EXPECT_TRUE((NotNull1(p1) >= NotNull1(p1)) == "true"); + EXPECT_TRUE((NotNull1(p1) >= NotNull2(p2)) == (p1 >= p2)); + EXPECT_TRUE((NotNull2(p2) >= NotNull1(p1)) == (p2 >= p1)); +} + +#if defined(__cplusplus) && (__cplusplus >= 201703L) + +template +static constexpr bool TypeDeductionCtorCompilesFor = false; +template +static constexpr bool + TypeDeductionCtorCompilesFor()})>> = true; + +template +static constexpr bool TypeDeductionHelperCompilesFor = false; +template +static constexpr bool + TypeDeductionHelperCompilesFor()}))>> = true; + +TEST(notnull_tests, TestNotNullConstructorTypeDeduction) +{ + { + int i = 42; + + not_null x{&i}; + helper(not_null{&i}); + helper_const(not_null{&i}); + + EXPECT_TRUE(*x == 42); + } + + { + const int i = 42; + + not_null x{&i}; + static_assert(TypeDeductionHelperCompilesFor, "TypeDeductionHelperCompilesFor"); + static_assert(!TypeDeductionHelperCompilesFor, + "!TypeDeductionHelperCompilesFor"); + helper_const(not_null{&i}); + + EXPECT_TRUE(*x == 42); + } + + { + int i = 42; + int* p = &i; + + not_null x{p}; + helper(not_null{p}); + helper_const(not_null{p}); + + EXPECT_TRUE(*x == 42); + } + + { + const int i = 42; + const int* p = &i; + + not_null x{p}; + helper_const(not_null{p}); + + EXPECT_TRUE(*x == 42); + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. TestNotNullConstructorTypeDeduction"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + { + auto workaround_macro = []() { + int* p1 = nullptr; + const not_null x{p1}; + }; + EXPECT_DEATH(workaround_macro(), expected); + } + + { + auto workaround_macro = []() { + const int* p1 = nullptr; + const not_null x{p1}; + }; + EXPECT_DEATH(workaround_macro(), expected); + } + + { + int* p = nullptr; + + EXPECT_DEATH(helper(not_null{p}), expected); + EXPECT_DEATH(helper_const(not_null{p}), expected); + } + + static_assert(TypeDeductionCtorCompilesFor, "TypeDeductionCtorCompilesFor"); +#if defined(_MSC_VER) && !defined(__clang__) + // Fails on gcc, clang, xcode, VS clang with + // "error : no type named 'type' in 'std::enable_if'; 'enable_if' cannot be used to + // disable this declaration" + static_assert(!TypeDeductionCtorCompilesFor, + "!TypeDeductionCtorCompilesFor"); + static_assert(!TypeDeductionHelperCompilesFor, + "!TypeDeductionHelperCompilesFor"); +#endif +} + +TEST(notnull_tests, TestVariantEmplace) +{ + int i = 0; + std::variant> v; + v.emplace>(&i); + + EXPECT_FALSE(v.valueless_by_exception()); + EXPECT_TRUE(v.index() == 1); + EXPECT_TRUE(std::get>(v) == &i); +} +#endif // #if defined(__cplusplus) && (__cplusplus >= 201703L) + +template +static constexpr bool HelperCompilesFor = false; +template +static constexpr bool HelperCompilesFor()))>> = true; + +TEST(notnull_tests, TestMakeNotNull) +{ + { + int i = 42; + + const auto x = make_not_null(&i); + helper(make_not_null(&i)); + helper_const(make_not_null(&i)); + + EXPECT_TRUE(*x == 42); + } + + { + const int i = 42; + + const auto x = make_not_null(&i); + static_assert(HelperCompilesFor>, + "HelperCompilesFor>"); + helper_const(make_not_null(&i)); + + EXPECT_TRUE(*x == 42); + } + + { + int i = 42; + int* p = &i; + + const auto x = make_not_null(p); + helper(make_not_null(p)); + helper_const(make_not_null(p)); + + EXPECT_TRUE(*x == 42); + } + + { + const int i = 42; + const int* p = &i; + + const auto x = make_not_null(p); + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); + helper_const(make_not_null(p)); + + EXPECT_TRUE(*x == 42); + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. TestMakeNotNull"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + { + const auto workaround_macro = []() { + int* p1 = nullptr; + const auto x = make_not_null(p1); + EXPECT_TRUE(*x == 42); + }; + EXPECT_DEATH(workaround_macro(), expected); + } + + { + const auto workaround_macro = []() { + const int* p1 = nullptr; + const auto x = make_not_null(p1); + EXPECT_TRUE(*x == 42); + }; + EXPECT_DEATH(workaround_macro(), expected); + } + + { + int* p = nullptr; + + EXPECT_DEATH(helper(make_not_null(p)), expected); + EXPECT_DEATH(helper_const(make_not_null(p)), expected); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + EXPECT_DEATH(make_not_null(nullptr), expected); + EXPECT_DEATH(helper(make_not_null(nullptr)), expected); + EXPECT_DEATH(helper_const(make_not_null(nullptr)), expected); + } +#endif +} + +TEST(notnull_tests, TestStdHash) +{ + { + int x = 42; + int y = 99; + not_null nn{&x}; + const not_null cnn{&x}; + + std::hash> hash_nn; + std::hash hash_intptr; + + EXPECT_TRUE(hash_nn(nn) == hash_intptr(&x)); + EXPECT_FALSE(hash_nn(nn) == hash_intptr(&y)); + EXPECT_FALSE(hash_nn(nn) == hash_intptr(nullptr)); + } + + { + const int x = 42; + const int y = 99; + not_null nn{&x}; + const not_null cnn{&x}; + + std::hash> hash_nn; + std::hash hash_intptr; + + EXPECT_TRUE(hash_nn(nn) == hash_intptr(&x)); + EXPECT_FALSE(hash_nn(nn) == hash_intptr(&y)); + EXPECT_FALSE(hash_nn(nn) == hash_intptr(nullptr)); + } + + { + auto x = std::make_shared(42); + auto y = std::make_shared(99); + not_null> nn{x}; + const not_null> cnn{x}; + + std::hash>> hash_nn; + std::hash> hash_sharedptr; + + EXPECT_TRUE(hash_nn(nn) == hash_sharedptr(x)); + EXPECT_FALSE(hash_nn(nn) == hash_sharedptr(y)); + EXPECT_TRUE(hash_nn(cnn) == hash_sharedptr(x)); + } +} diff --git a/kernel/third_party/GSL-5.0.0/tests/owner_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/owner_tests.cpp new file mode 100644 index 0000000..c6373d2 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/owner_tests.cpp @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include + +#include // for owner +#include // for declval + +using namespace gsl; + +GSL_SUPPRESS(f.23) +void f(int* i) { *i += 1; } + +TEST(owner_tests, basic_test) +{ + owner p = new int(120); + EXPECT_TRUE(*p == 120); + f(p); + EXPECT_TRUE(*p == 121); + delete p; +} + +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + +template +static constexpr bool OwnerCompilesFor = false; +template +static constexpr bool OwnerCompilesFor{})>> = true; +static_assert(OwnerCompilesFor, "OwnerCompilesFor"); +static_assert(!OwnerCompilesFor, "!OwnerCompilesFor"); +static_assert(!OwnerCompilesFor>, "!OwnerCompilesFor>"); diff --git a/kernel/third_party/GSL-5.0.0/tests/pointers_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/pointers_tests.cpp new file mode 100644 index 0000000..72f761f --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/pointers_tests.cpp @@ -0,0 +1,119 @@ +#include + +#include + +#include +#include +#include +#include + +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + +namespace +{ +// Custom pointer type that can be used for gsl::not_null, but for which these cannot be swapped. +struct NotMoveAssignableCustomPtr +{ + NotMoveAssignableCustomPtr() = default; + NotMoveAssignableCustomPtr(const NotMoveAssignableCustomPtr&) = default; + NotMoveAssignableCustomPtr& operator=(const NotMoveAssignableCustomPtr&) = default; + NotMoveAssignableCustomPtr(NotMoveAssignableCustomPtr&&) = default; + NotMoveAssignableCustomPtr& operator=(NotMoveAssignableCustomPtr&&) = delete; + + bool operator!=(std::nullptr_t) const { return true; } + + int dummy{}; // Without this clang warns, that NotMoveAssignableCustomPtr() is unneeded +}; + +template +static constexpr bool SwapCompilesFor = false; +template +static constexpr bool + SwapCompilesFor(std::declval&>(), + std::declval&>()))>> = true; + +TEST(pointers_test, swap) +{ + // taken from gh-1129: + { + gsl::not_null> a(std::make_unique(0)); + gsl::not_null> b(std::make_unique(1)); + + static_assert(noexcept(gsl::swap(a, b)), + "not null unique_ptr should be noexcept-swappable"); + + EXPECT_TRUE(*a == 0); + EXPECT_TRUE(*b == 1); + + gsl::swap(a, b); + + EXPECT_TRUE(*a == 1); + EXPECT_TRUE(*b == 0); + + // Make sure our custom ptr can be used with not_null. The shared_pr is to prevent "unused" + // compiler warnings. + const auto shared_custom_ptr{std::make_shared()}; + gsl::not_null c{*shared_custom_ptr}; + EXPECT_TRUE(c.get() != nullptr); + } + + { + gsl::strict_not_null> a{std::make_unique(0)}; + gsl::strict_not_null> b{std::make_unique(1)}; + + static_assert(noexcept(gsl::swap(a, b)), + "strict not null unique_ptr should be noexcept-swappable"); + + EXPECT_TRUE(*a == 0); + EXPECT_TRUE(*b == 1); + + gsl::swap(a, b); + + EXPECT_TRUE(*a == 1); + EXPECT_TRUE(*b == 0); + } + + { + gsl::not_null> a{std::make_unique(0)}; + gsl::strict_not_null> b{std::make_unique(1)}; + + EXPECT_TRUE(*a == 0); + EXPECT_TRUE(*b == 1); + + gsl::swap(a, b); + + EXPECT_TRUE(*a == 1); + EXPECT_TRUE(*b == 0); + } + + static_assert(!SwapCompilesFor, + "!SwapCompilesFor"); +} + +TEST(pointers_test, member_types) +{ + static_assert(std::is_same::element_type, int*>::value, + "check member type: element_type"); +} + +TEST(pointers_test, hash_noexcept_compiles) +{ + { + using Key = gsl::not_null>; + static_assert(noexcept(std::hash{}(std::declval())), + "gsl::not_null hash operator must be noexcept"); + } + + { + using Key = gsl::strict_not_null>; + static_assert(noexcept(std::hash{}(std::declval())), + "gsl::strict_not_null hash operator must be noexcept"); + } +} + +} // namespace diff --git a/kernel/third_party/GSL-5.0.0/tests/span_compatibility_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/span_compatibility_tests.cpp new file mode 100644 index 0000000..e69e0aa --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/span_compatibility_tests.cpp @@ -0,0 +1,1043 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include + +#include // for byte +#include // for span, span_iterator, operator==, operator!= + +#include // for array +#include // for ptrdiff_t +#include // for reverse_iterator, operator-, operator== +#include // for integral_constant<>::value, is_default_co... +#include +#include // for vector + +using namespace std; +using namespace gsl; + +// Below are tests that verify the gsl interface support the same things as the std +// Ranges and Concepts support need to be added later. + +struct Base +{ +}; +struct Derived : Base +{ +}; +static_assert(std::is_convertible::value, "std::is_convertible"); +static_assert(!std::is_convertible::value, + "!std::is_convertible"); + +// int*(*) [], int const* const(*)[] was identified as an issue in CWG330 and the resolution was +// provided with N4261. +template +void ArrayConvertibilityCheck() +{ +#if __cplusplus >= 201703l + if constexpr (std::is_convertible::value) + { + std::array stl_nullptr{{nullptr, nullptr, nullptr}}; + gsl::span sp_const_nullptr_1{stl_nullptr}; + EXPECT_TRUE(sp_const_nullptr_1.data() == stl_nullptr.data()); + EXPECT_TRUE(sp_const_nullptr_1.size() == 3); + + gsl::span sp_const_nullptr_2{std::as_const(stl_nullptr)}; + EXPECT_TRUE(sp_const_nullptr_2.data() == stl_nullptr.data()); + EXPECT_TRUE(sp_const_nullptr_2.size() == 3); + + static_assert(std::is_same>::value, + "std::is_same< decltype(span{stl_nullptr}), span>::value"); + static_assert(std::is_same>::value, + "std::is_same< decltype(span{std::as_const(stl_nullptr)}), span>::value"); + } +#endif +} + +TEST(span_compatibility_tests, assertion_tests) +{ + int arr[3]{10, 20, 30}; + std::array stl{{100, 200, 300}}; + + ArrayConvertibilityCheck(); + + { + gsl::span sp_dyn; + EXPECT_TRUE(sp_dyn.data() == nullptr); + EXPECT_TRUE(sp_dyn.size() == 0); + EXPECT_TRUE(sp_dyn.empty()); + } + { + gsl::span sp_zero; + EXPECT_TRUE(sp_zero.data() == nullptr); + EXPECT_TRUE(sp_zero.size() == 0); + EXPECT_TRUE(sp_zero.empty()); + + gsl::span sp_dyn_a(arr, 3); + gsl::span sp_dyn_b(begin(arr), 3); + EXPECT_TRUE(sp_dyn_a.data() == std::begin(arr)); + EXPECT_TRUE(sp_dyn_b.data() == std::begin(arr)); + EXPECT_TRUE(sp_dyn_a.size() == 3); + EXPECT_TRUE(sp_dyn_b.size() == 3); + + gsl::span sp_three_a(arr, 3); + gsl::span sp_three_b(begin(arr), 3); + EXPECT_TRUE(sp_three_a.data() == std::begin(arr)); + EXPECT_TRUE(sp_three_b.data() == std::begin(arr)); + EXPECT_TRUE(sp_three_a.size() == 3); + EXPECT_TRUE(sp_three_b.size() == 3); + + gsl::span sp_const_a(arr, 3); + gsl::span sp_const_b(begin(arr), 3); + EXPECT_TRUE(sp_const_a.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_b.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_a.size() == 3); + EXPECT_TRUE(sp_const_b.size() == 3); + +#if __cplusplus >= 201703l + gsl::span sp_const_c(std::as_const(arr), 3); + EXPECT_TRUE(sp_const_c.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_c.size() == 3); +#endif // __cplusplus >= 201703l + + gsl::span sp_const_d(cbegin(arr), 3); + EXPECT_TRUE(sp_const_d.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_d.size() == 3); + } + { + gsl::span sp_dyn_a(begin(arr), std::end(arr)); + EXPECT_TRUE(sp_dyn_a.data() == std::begin(arr)); + EXPECT_TRUE(sp_dyn_a.size() == 3); + + gsl::span sp_three_a(begin(arr), std::end(arr)); + EXPECT_TRUE(sp_three_a.data() == std::begin(arr)); + EXPECT_TRUE(sp_three_a.size() == 3); + + gsl::span sp_const_a(begin(arr), std::end(arr)); + gsl::span sp_const_b(begin(arr), std::cend(arr)); + gsl::span sp_const_c(cbegin(arr), std::end(arr)); + gsl::span sp_const_d(cbegin(arr), std::cend(arr)); + EXPECT_TRUE(sp_const_a.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_b.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_c.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_d.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_a.size() == 3); + EXPECT_TRUE(sp_const_b.size() == 3); + EXPECT_TRUE(sp_const_c.size() == 3); + EXPECT_TRUE(sp_const_d.size() == 3); + } + { + gsl::span sp_dyn_a(arr); + gsl::span sp_dyn_b(stl); + gsl::span sp_dyn_c{stl}; + gsl::span sp_dyn_d{stl}; + EXPECT_TRUE(sp_dyn_a.data() == std::begin(arr)); + EXPECT_TRUE(sp_dyn_b.data() == stl.data()); + EXPECT_TRUE(sp_dyn_a.size() == 3); + EXPECT_TRUE(sp_dyn_b.size() == 3); + + gsl::span sp_three_a(arr); + gsl::span sp_three_b(stl); + EXPECT_TRUE(sp_three_a.data() == std::begin(arr)); + EXPECT_TRUE(sp_three_b.data() == stl.data()); + EXPECT_TRUE(sp_three_a.size() == 3); + EXPECT_TRUE(sp_three_b.size() == 3); + + gsl::span sp_const_w(arr); + gsl::span sp_const_y(stl); + EXPECT_TRUE(sp_const_w.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_y.data() == stl.data()); + EXPECT_TRUE(sp_const_w.size() == 3); + EXPECT_TRUE(sp_const_y.size() == 3); + +#if __cplusplus >= 201703l + gsl::span sp_const_x(std::as_const(arr)); + EXPECT_TRUE(sp_const_x.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_x.size() == 3); + + gsl::span sp_const_z(std::as_const(stl)); + EXPECT_TRUE(sp_const_z.data() == stl.data()); + EXPECT_TRUE(sp_const_z.size() == 3); +#endif // __cplusplus >= 201703l + } + { + const gsl::span orig_dyn(arr); + const gsl::span orig_three(arr); + const gsl::span orig_const_dyn(arr); + const gsl::span orig_const_three(arr); + + gsl::span sp_a(orig_dyn); + gsl::span sp_b(orig_three); + + gsl::span sp_c(orig_three); + + gsl::span sp_d(orig_dyn); + gsl::span sp_e(orig_three); + gsl::span sp_f(orig_const_dyn); + gsl::span sp_g(orig_const_three); + + gsl::span sp_h(orig_three); + gsl::span sp_i(orig_const_three); + + EXPECT_TRUE(sp_a.data() == std::begin(arr)); + EXPECT_TRUE(sp_b.data() == std::begin(arr)); + EXPECT_TRUE(sp_c.data() == std::begin(arr)); + EXPECT_TRUE(sp_d.data() == std::begin(arr)); + EXPECT_TRUE(sp_e.data() == std::begin(arr)); + EXPECT_TRUE(sp_f.data() == std::begin(arr)); + EXPECT_TRUE(sp_g.data() == std::begin(arr)); + EXPECT_TRUE(sp_h.data() == std::begin(arr)); + EXPECT_TRUE(sp_i.data() == std::begin(arr)); + EXPECT_TRUE(sp_a.size() == 3); + EXPECT_TRUE(sp_b.size() == 3); + EXPECT_TRUE(sp_c.size() == 3); + EXPECT_TRUE(sp_d.size() == 3); + EXPECT_TRUE(sp_e.size() == 3); + EXPECT_TRUE(sp_f.size() == 3); + EXPECT_TRUE(sp_g.size() == 3); + EXPECT_TRUE(sp_h.size() == 3); + EXPECT_TRUE(sp_i.size() == 3); + } + { + gsl::span sp_dyn(arr); + gsl::span sp_three(arr); + gsl::span sp_const_dyn(arr); + gsl::span sp_const_three(arr); + + EXPECT_TRUE(sp_dyn.data() == std::begin(arr)); + EXPECT_TRUE(sp_three.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_dyn.data() == std::begin(arr)); + EXPECT_TRUE(sp_const_three.data() == std::begin(arr)); + EXPECT_TRUE(sp_dyn.size() == 3); + EXPECT_TRUE(sp_three.size() == 3); + EXPECT_TRUE(sp_const_dyn.size() == 3); + EXPECT_TRUE(sp_const_three.size() == 3); + + int other[4]{12, 34, 56, 78}; + + sp_dyn = gsl::span{other}; + sp_three = gsl::span{stl}; + sp_const_dyn = gsl::span{other}; + sp_const_three = gsl::span{stl}; + + EXPECT_TRUE(sp_dyn.data() == std::begin(other)); + EXPECT_TRUE(sp_three.data() == stl.data()); + EXPECT_TRUE(sp_const_dyn.data() == std::begin(other)); + EXPECT_TRUE(sp_const_three.data() == stl.data()); + EXPECT_TRUE(sp_dyn.size() == 4); + EXPECT_TRUE(sp_three.size() == 3); + EXPECT_TRUE(sp_const_dyn.size() == 4); + EXPECT_TRUE(sp_const_three.size() == 3); + } + { + gsl::span::iterator it_dyn{}; + + { + gsl::span sp_dyn(arr); + it_dyn = sp_dyn.begin(); + } + + EXPECT_TRUE(*it_dyn == arr[0]); + EXPECT_TRUE(it_dyn[2] == arr[2]); + + gsl::span::iterator it_three{}; + + { + gsl::span sp_three(stl); + it_three = sp_three.begin(); + } + + EXPECT_TRUE(*it_three == stl[0]); + EXPECT_TRUE(it_three[2] == stl[2]); + } + + { + int sequence[9]{10, 20, 30, 40, 50, 60, 70, 80, 90}; + + const gsl::span sp_dyn(sequence); + const gsl::span sp_nine(sequence); + + auto first_3 = sp_dyn.first<3>(); + auto first_4 = sp_nine.first<4>(); + auto first_5 = sp_dyn.first(5); + auto first_6 = sp_nine.first(6); + static_assert(noexcept(sp_dyn.first<3>()), "noexcept(sp_dyn.first<3>())"); // strengthened + static_assert(noexcept(sp_nine.first<4>()), "noexcept(sp_nine.first<4>())"); // strengthened + static_assert(noexcept(sp_dyn.first(5)), "noexcept(sp_dyn.first(5))"); // strengthened + static_assert(noexcept(sp_nine.first(6)), "noexcept(sp_nine.first(6))"); // strengthened + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + EXPECT_TRUE(first_3.data() == std::begin(sequence)); + EXPECT_TRUE(first_4.data() == std::begin(sequence)); + EXPECT_TRUE(first_5.data() == std::begin(sequence)); + EXPECT_TRUE(first_6.data() == std::begin(sequence)); + EXPECT_TRUE(first_3.size() == 3); + EXPECT_TRUE(first_4.size() == 4); + EXPECT_TRUE(first_5.size() == 5); + EXPECT_TRUE(first_6.size() == 6); + + auto last_3 = sp_dyn.last<3>(); + auto last_4 = sp_nine.last<4>(); + auto last_5 = sp_dyn.last(5); + auto last_6 = sp_nine.last(6); + static_assert(noexcept(sp_dyn.last<3>()), "noexcept(sp_dyn.last<3>())"); // strengthened + static_assert(noexcept(sp_nine.last<4>()), "noexcept(sp_nine.last<4>())"); // strengthened + static_assert(noexcept(sp_dyn.last(5)), "noexcept(sp_dyn.last(5))"); // strengthened + static_assert(noexcept(sp_nine.last(6)), "noexcept(sp_nine.last(6))"); // strengthened + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + EXPECT_TRUE(last_3.data() == std::begin(sequence) + 6); + EXPECT_TRUE(last_4.data() == std::begin(sequence) + 5); + EXPECT_TRUE(last_5.data() == std::begin(sequence) + 4); + EXPECT_TRUE(last_6.data() == std::begin(sequence) + 3); + EXPECT_TRUE(last_3.size() == 3); + EXPECT_TRUE(last_4.size() == 4); + EXPECT_TRUE(last_5.size() == 5); + EXPECT_TRUE(last_6.size() == 6); + + auto offset_3 = sp_dyn.subspan<3>(); + auto offset_4 = sp_nine.subspan<4>(); + auto offset_5 = sp_dyn.subspan(5); + auto offset_6 = sp_nine.subspan(6); + static_assert(noexcept(sp_dyn.subspan<3>()), + "noexcept(sp_dyn.subspan<3>())"); // strengthened + static_assert(noexcept(sp_nine.subspan<4>()), + "noexcept(sp_nine.subspan<4>())"); // strengthened + static_assert(noexcept(sp_dyn.subspan(5)), "noexcept(sp_dyn.subspan(5))"); // strengthened + static_assert(noexcept(sp_nine.subspan(6)), "noexcept(sp_nine.subspan(6))"); // strengthened + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + EXPECT_TRUE(offset_3.data() == std::begin(sequence) + 3); + EXPECT_TRUE(offset_4.data() == std::begin(sequence) + 4); + EXPECT_TRUE(offset_5.data() == std::begin(sequence) + 5); + EXPECT_TRUE(offset_6.data() == std::begin(sequence) + 6); + EXPECT_TRUE(offset_3.size() == 6); + EXPECT_TRUE(offset_4.size() == 5); + EXPECT_TRUE(offset_5.size() == 4); + EXPECT_TRUE(offset_6.size() == 3); + + auto subspan_3 = sp_dyn.subspan<3, 2>(); + auto subspan_4 = sp_nine.subspan<4, 2>(); + auto subspan_5 = sp_dyn.subspan(5, 2); + auto subspan_6 = sp_nine.subspan(6, 2); + static_assert(noexcept(sp_dyn.subspan<3, 2>()), + "noexcept(sp_dyn.subspan<3, 2>())"); // strengthened + static_assert(noexcept(sp_nine.subspan<4, 2>()), + "noexcept(sp_nine.subspan<4, 2>())"); // strengthened + static_assert(noexcept(sp_dyn.subspan(5, 2)), + "noexcept(sp_dyn.subspan(5, 2))"); // strengthened + static_assert(noexcept(sp_nine.subspan(6, 2)), + "noexcept(sp_nine.subspan(6, 2))"); // strengthened + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + EXPECT_TRUE(subspan_3.data() == std::begin(sequence) + 3); + EXPECT_TRUE(subspan_4.data() == std::begin(sequence) + 4); + EXPECT_TRUE(subspan_5.data() == std::begin(sequence) + 5); + EXPECT_TRUE(subspan_6.data() == std::begin(sequence) + 6); + EXPECT_TRUE(subspan_3.size() == 2); + EXPECT_TRUE(subspan_4.size() == 2); + EXPECT_TRUE(subspan_5.size() == 2); + EXPECT_TRUE(subspan_6.size() == 2); + + static_assert(noexcept(sp_dyn.size()), "noexcept(sp_dyn.size())"); + static_assert(noexcept(sp_dyn.size_bytes()), "noexcept(sp_dyn.size_bytes())"); + static_assert(noexcept(sp_dyn.empty()), "noexcept(sp_dyn.empty())"); + static_assert(noexcept(sp_dyn[0]), "noexcept(sp_dyn[0])"); // strengthened + static_assert(noexcept(sp_dyn.front()), "noexcept(sp_dyn.front())"); // strengthened + static_assert(noexcept(sp_dyn.back()), "noexcept(sp_dyn.back())"); // strengthened + static_assert(noexcept(sp_dyn.data()), "noexcept(sp_dyn.data())"); + static_assert(noexcept(sp_dyn.begin()), "noexcept(sp_dyn.begin())"); + static_assert(noexcept(sp_dyn.end()), "noexcept(sp_dyn.end())"); + static_assert(noexcept(sp_dyn.rbegin()), "noexcept(sp_dyn.rbegin())"); + static_assert(noexcept(sp_dyn.rend()), "noexcept(sp_dyn.rend())"); + + static_assert(noexcept(sp_nine.size()), "noexcept(sp_nine.size())"); + static_assert(noexcept(sp_nine.size_bytes()), "noexcept(sp_nine.size_bytes())"); + static_assert(noexcept(sp_nine.empty()), "noexcept(sp_nine.empty())"); + static_assert(noexcept(sp_nine[0]), "noexcept(sp_nine[0])"); // strengthened + static_assert(noexcept(sp_nine.front()), "noexcept(sp_nine.front())"); // strengthened + static_assert(noexcept(sp_nine.back()), "noexcept(sp_nine.back())"); // strengthened + static_assert(noexcept(sp_nine.data()), "noexcept(sp_nine.data())"); + static_assert(noexcept(sp_nine.begin()), "noexcept(sp_nine.begin())"); + static_assert(noexcept(sp_nine.end()), "noexcept(sp_nine.end())"); + static_assert(noexcept(sp_nine.rbegin()), "noexcept(sp_nine.rbegin())"); + static_assert(noexcept(sp_nine.rend()), "noexcept(sp_nine.rend())"); + + EXPECT_TRUE(sp_dyn.size() == 9); + EXPECT_TRUE(sp_nine.size() == 9); + + EXPECT_TRUE(sp_dyn.size_bytes() == 9 * sizeof(int)); + EXPECT_TRUE(sp_nine.size_bytes() == 9 * sizeof(int)); + + EXPECT_TRUE(!sp_dyn.empty()); + EXPECT_TRUE(!sp_nine.empty()); + + EXPECT_TRUE(sp_dyn[0] == 10); + EXPECT_TRUE(sp_nine[0] == 10); + EXPECT_TRUE(sp_dyn[8] == 90); + EXPECT_TRUE(sp_nine[8] == 90); + + EXPECT_TRUE(sp_dyn.front() == 10); + EXPECT_TRUE(sp_nine.front() == 10); + + EXPECT_TRUE(sp_dyn.back() == 90); + EXPECT_TRUE(sp_nine.back() == 90); + + EXPECT_TRUE(&sp_dyn.front() == std::begin(sequence)); + EXPECT_TRUE(&sp_nine.front() == std::begin(sequence)); + EXPECT_TRUE(&sp_dyn[4] == std::begin(sequence) + 4); + EXPECT_TRUE(&sp_nine[4] == std::begin(sequence) + 4); + EXPECT_TRUE(&sp_dyn.back() == std::begin(sequence) + 8); + EXPECT_TRUE(&sp_nine.back() == std::begin(sequence) + 8); + + EXPECT_TRUE(sp_dyn.data() == std::begin(sequence)); + EXPECT_TRUE(sp_nine.data() == std::begin(sequence)); + + EXPECT_TRUE(*sp_dyn.begin() == 10); + EXPECT_TRUE(*sp_nine.begin() == 10); + + EXPECT_TRUE(sp_dyn.end()[-2] == 80); + EXPECT_TRUE(sp_nine.end()[-2] == 80); + + EXPECT_TRUE(*sp_dyn.rbegin() == 90); + EXPECT_TRUE(*sp_nine.rbegin() == 90); + + EXPECT_TRUE(sp_dyn.rend()[-2] == 20); + EXPECT_TRUE(sp_nine.rend()[-2] == 20); + + static_assert(is_same::iterator>::value, + "is_same::iterator>::value"); + static_assert(is_same::iterator>::value, + "is_same::iterator>::value"); + static_assert(is_same::iterator>::value, + "is_same::iterator>::value"); + static_assert(is_same::iterator>::value, + "is_same::iterator>::value"); + static_assert( + is_same::reverse_iterator>::value, + "is_same::reverse_iterator>::value"); + static_assert( + is_same::reverse_iterator>::value, + "is_same::reverse_iterator>::value"); + static_assert(is_same::reverse_iterator>::value, + "is_same::reverse_iterator>::value"); + static_assert( + is_same::reverse_iterator>::value, + "is_same::reverse_iterator>::value"); + } + { + int sequence[9]{10, 20, 30, 40, 50, 60, 70, 80, 90}; + + constexpr size_t SizeBytes = sizeof(sequence); + + const gsl::span sp_dyn(sequence); + const gsl::span sp_nine(sequence); + const gsl::span sp_const_dyn(sequence); + const gsl::span sp_const_nine(sequence); + + static_assert(noexcept(as_bytes(sp_dyn)), "noexcept(as_bytes(sp_dyn))"); + static_assert(noexcept(as_bytes(sp_nine)), "noexcept(as_bytes(sp_nine))"); + static_assert(noexcept(as_bytes(sp_const_dyn)), "noexcept(as_bytes(sp_const_dyn))"); + static_assert(noexcept(as_bytes(sp_const_nine)), "noexcept(as_bytes(sp_const_nine))"); + static_assert(noexcept(as_writable_bytes(sp_dyn)), "noexcept(as_writable_bytes(sp_dyn))"); + static_assert(noexcept(as_writable_bytes(sp_nine)), "noexcept(as_writable_bytes(sp_nine))"); + + auto sp_1 = as_bytes(sp_dyn); + auto sp_2 = as_bytes(sp_nine); + auto sp_3 = as_bytes(sp_const_dyn); + auto sp_4 = as_bytes(sp_const_nine); + auto sp_5 = as_writable_bytes(sp_dyn); + auto sp_6 = as_writable_bytes(sp_nine); + + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + static_assert(is_same>::value, + "is_same>::value"); + + EXPECT_TRUE(sp_1.data() == reinterpret_cast(begin(sequence))); + EXPECT_TRUE(sp_2.data() == reinterpret_cast(begin(sequence))); + EXPECT_TRUE(sp_3.data() == reinterpret_cast(begin(sequence))); + EXPECT_TRUE(sp_4.data() == reinterpret_cast(begin(sequence))); + EXPECT_TRUE(sp_5.data() == reinterpret_cast(begin(sequence))); + EXPECT_TRUE(sp_6.data() == reinterpret_cast(begin(sequence))); + + EXPECT_TRUE(sp_1.size() == SizeBytes); + EXPECT_TRUE(sp_2.size() == SizeBytes); + EXPECT_TRUE(sp_3.size() == SizeBytes); + EXPECT_TRUE(sp_4.size() == SizeBytes); + EXPECT_TRUE(sp_5.size() == SizeBytes); + EXPECT_TRUE(sp_6.size() == SizeBytes); + } +} + +// assertions for span's definition +static_assert(std::is_same::value, + "gsl::dynamic_extent must be represented as std::size_t"); +static_assert(gsl::dynamic_extent == static_cast(-1), + "gsl::dynamic_extent must be defined as the max value of std::size_t"); + +static_assert(std::is_same::extent), const std::size_t>::value, + "Ensure that the type of gsl::span::extent is std::size_t"); +static_assert(gsl::span::extent == gsl::dynamic_extent, + "gsl::span::extent should be equivalent to gsl::dynamic_extent"); + +static_assert(std::is_same::extent), const std::size_t>::value, + "Ensure that the type of gsl::span::extent is std::size_t"); +static_assert(gsl::span::extent == 3, "Ensure that span::extent is equal to 3"); + +static_assert(std::is_same::element_type, int>::value, + "span::element_type should be int"); +static_assert(std::is_same::value_type, int>::value, + "span::value_type should be int"); +static_assert(std::is_same::size_type, std::size_t>::value, + "span::size_type should be std::size_t"); +static_assert(std::is_same::difference_type, ptrdiff_t>::value, + "span::difference_type should be std::ptrdiff_t"); +static_assert(std::is_same::pointer, int*>::value, + "span::pointer should be int*"); +static_assert(std::is_same::const_pointer, const int*>::value, + "span::const_pointer should be const int*"); +static_assert(std::is_same::reference, int&>::value, + "span::reference should be int&"); +static_assert(std::is_same::const_reference, const int&>::value, + "span::const_reference should be const int&"); + +static_assert(std::is_same::element_type, int>::value, + "span::element_type should be int"); +static_assert(std::is_same::value_type, int>::value, + "span::value_type should be int"); +static_assert(std::is_same::size_type, std::size_t>::value, + "span::size_type should be std::size_t"); +static_assert(std::is_same::difference_type, ptrdiff_t>::value, + "span::difference_type should be std::ptrdiff_t"); +static_assert(std::is_same::pointer, int*>::value, + "span::pointer should be int*"); +static_assert(std::is_same::const_pointer, const int*>::value, + "span::const_pointer should be const int*"); +static_assert(std::is_same::reference, int&>::value, + "span::reference should be int&"); +static_assert(std::is_same::const_reference, const int&>::value, + "span::const_reference should be const int&"); + +static_assert(std::is_same::element_type, const int>::value, + "span::element_type should be const int"); +static_assert(std::is_same::value_type, int>::value, + "span::value_type should be int"); +static_assert(std::is_same::size_type, std::size_t>::value, + "span::size_type should be size_t"); +static_assert(std::is_same::difference_type, ptrdiff_t>::value, + "span::difference_type should be ptrdiff_t"); +static_assert(std::is_same::pointer, const int*>::value, + "span::pointer should be const int*"); +static_assert(std::is_same::const_pointer, const int*>::value, + "span::const_pointer should be const int*"); +static_assert(std::is_same::reference, const int&>::value, + "span::reference should be const int&"); +static_assert(std::is_same::const_reference, const int&>::value, + "span::const_reference should be const int&"); + +static_assert(std::is_same::element_type, const int>::value, + "span::element_type should be const int"); +static_assert(std::is_same::value_type, int>::value, + "span::value_type should be int"); +static_assert(std::is_same::size_type, std::size_t>::value, + "span::size_type should be size_t"); +static_assert(std::is_same::difference_type, ptrdiff_t>::value, + "span::difference_type should be ptrdiff_t"); +static_assert(std::is_same::pointer, const int*>::value, + "span::pointer should be const int*"); +static_assert(std::is_same::const_pointer, const int*>::value, + "span::const_pointer should be const int*"); +static_assert(std::is_same::reference, const int&>::value, + "span::reference should be const int&"); +static_assert(std::is_same::const_reference, const int&>::value, + "span::const_reference should be const int&"); + +// assertions for span_iterator +static_assert(std::is_convertible::iterator, gsl::span::iterator>::value, + "span::iterator should be implicitly convertible to span::iterator"); +static_assert(std::is_same::iterator>::pointer, int*>::value, + "span::iterator's pointer should be int*"); +static_assert( + std::is_same::reverse_iterator, + std::reverse_iterator::iterator>>::value, + "span::reverse_iterator should equal std::reverse_iterator::iterator>"); + +static_assert(std::is_same::iterator>::pointer, int*>::value, + "span::iterator's pointer should be int*"); +static_assert( + std::is_same::reverse_iterator, + std::reverse_iterator::iterator>>::value, + "span::reverse_iterator should equal std::reverse_iterator::iterator>"); + +static_assert( + std::is_same::iterator>::pointer, const int*>::value, + "span::iterator's pointer should be int*"); +static_assert(std::is_same::reverse_iterator, + std::reverse_iterator::iterator>>::value, + "span::reverse_iterator should equal std::reverse_iterator::iterator>"); + +static_assert(std::is_same::iterator>::pointer, + const int*>::value, + "span::iterator's pointer should be int*"); +static_assert(std::is_same::reverse_iterator, + std::reverse_iterator::iterator>>::value, + "span::reverse_iterator should equal std::reverse_iterator::iterator>"); + +// copyability assertions +static_assert(std::is_trivially_copyable>::value, + "span should be trivially copyable"); +static_assert(std::is_trivially_copyable::iterator>::value, + "span::iterator should be trivially copyable"); + +static_assert(std::is_trivially_copyable>::value, + "span should be trivially copyable"); +static_assert(std::is_trivially_copyable::iterator>::value, + "span::iterator should be trivially copyable"); + +static_assert(std::is_trivially_copyable>::value, + "span should be trivially copyable"); +static_assert(std::is_trivially_copyable::iterator>::value, + "span::iterator should be trivially copyable"); + +static_assert(std::is_trivially_copyable>::value, + "span should be trivially copyable"); +static_assert(std::is_trivially_copyable::iterator>::value, + "span::iterator should be trivially copyable"); + +static_assert(!std::is_constructible::iterator, gsl::span>::value, + "span::iterator should not be constructible from span"); +static_assert(!std::is_constructible::iterator, int*, int*, int*>::value, + "span::iterator should not be constructible from an arbitrary pointer triple"); +static_assert(!std::is_constructible::iterator, gsl::span>::value, + "span::iterator should not be constructible from span"); +static_assert( + !std::is_constructible::iterator, const int*, const int*, + const int*>::value, + "span::iterator should not be constructible from an arbitrary pointer triple"); +static_assert(std::is_copy_constructible::iterator>::value, + "span::iterator should remain copy constructible"); + +// nothrow constructible assertions +static_assert(std::is_nothrow_constructible, int*, std::size_t>::value, + "std::is_nothrow_constructible, int*, std::size_t>"); +static_assert(std::is_nothrow_constructible, int*, std::uint16_t>::value, + "std::is_nothrow_constructible, int*, std::uint16_t>"); +static_assert(std::is_nothrow_constructible, int*, int*>::value, + "std::is_nothrow_constructible, int*, int*>"); +static_assert(std::is_nothrow_constructible, int (&)[3]>::value, + "std::is_nothrow_constructible, int(&)[3]>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, std::array&>::value, + "std::is_nothrow_constructible, std::array&>"); + +static_assert(std::is_nothrow_constructible, int*, std::size_t>::value, + "std::is_nothrow_constructible, int*, std::size_t>"); +static_assert(std::is_nothrow_constructible, int*, std::uint16_t>::value, + "std::is_nothrow_constructible, int*, std::uint16_t>"); +static_assert(std::is_nothrow_constructible, int*, int*>::value, + "std::is_nothrow_constructible, int*, int*>"); +static_assert(std::is_nothrow_constructible, int (&)[3]>::value, + "std::is_nothrow_constructible, int(&)[3]>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, std::array&>::value, + "std::is_nothrow_constructible, std::array&>"); + +static_assert(std::is_nothrow_constructible, int*, std::size_t>::value, + "std::is_nothrow_constructible, int*, std::size_t>"); +static_assert(std::is_nothrow_constructible, int*, int*>::value, + "std::is_nothrow_constructible, int*, int*>"); +static_assert(std::is_nothrow_constructible, int*, const int*>::value, + "std::is_nothrow_constructible, int*, const int*>"); +static_assert(std::is_nothrow_constructible, int (&)[3]>::value, + "std::is_nothrow_constructible, int(&)[3]>"); +static_assert(std::is_nothrow_constructible, const int*, int*>::value, + "std::is_nothrow_constructible, const int*, int*>"); +static_assert(std::is_nothrow_constructible, const int*, const int*>::value, + "std::is_nothrow_constructible, const int*, const int*>"); +static_assert(std::is_nothrow_constructible, const int*, std::size_t>::value, + "std::is_nothrow_constructible, const int*, std::size_t>"); +static_assert(std::is_nothrow_constructible, const int (&)[3]>::value, + "std::is_nothrow_constructible, const int(&)[3]>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, std::array&>::value, + "std::is_nothrow_constructible, std::array&>"); +static_assert(std::is_nothrow_constructible, const std::array&>::value, + "std::is_nothrow_constructible, const std::array&>"); + +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); + +static_assert(std::is_nothrow_constructible, Base (&)[3]>::value, + "std::is_nothrow_constructible, Base(&)[3]>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, std::array&>::value, + "std::is_nothrow_constructible, std::array&>"); + +static_assert(std::is_nothrow_constructible, Base (&)[3]>::value, + "std::is_nothrow_constructible, Base(&)[3]>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, std::array&>::value, + "std::is_nothrow_constructible, std::array&>"); + +static_assert(std::is_nothrow_constructible, Base (&)[3]>::value, + "std::is_nothrow_constructible, Base(&)[3]>"); +static_assert(std::is_nothrow_constructible, const Base (&)[3]>::value, + "std::is_nothrow_constructible, const Base(&)[3]>"); +static_assert(std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert(std::is_nothrow_constructible, std::array&>::value, + "std::is_nothrow_constructible, std::array&>"); +static_assert( + std::is_nothrow_constructible, const std::array&>::value, + "std::is_nothrow_constructible, const std::array&>"); + +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); +static_assert( + std::is_nothrow_constructible, const gsl::span&>::value, + "std::is_nothrow_constructible, const gsl::span&>"); + +// non-constructible assertions +static_assert(!std::is_constructible, const int*, int*>::value, + "!std::is_constructible, const int*, int*>"); +static_assert(!std::is_constructible, const int*, const int*>::value, + "!std::is_constructible, const int*, const int*>"); +static_assert(!std::is_constructible, const int*, double*>::value, + "!std::is_constructible, const int*, double*>"); +static_assert(!std::is_constructible, const int*, std::size_t>::value, + "!std::is_constructible, const int*, std::size_t>"); +static_assert(!std::is_constructible, const int (&)[3]>::value, + "!std::is_constructible, const int(&)[3]>"); +static_assert(!std::is_constructible, double*, int*>::value, + "!std::is_constructible, double*, int*>"); +static_assert(!std::is_constructible, double*, const int*>::value, + "!std::is_constructible, double*, const int*>"); +static_assert(!std::is_constructible, double*, double*>::value, + "!std::is_constructible, double*, double*>"); +static_assert(!std::is_constructible, double*, std::size_t>::value, + "!std::is_constructible, double*, std::size_t>"); +static_assert(!std::is_constructible, double (&)[3]>::value, + "!std::is_constructible, double(&)[3]>"); +static_assert(!std::is_constructible, int*, double*>::value, + "!std::is_constructible, int*, double*>"); +static_assert(!std::is_constructible, std::size_t, int*>::value, + "!std::is_constructible, std::size_t, int*>"); +static_assert(!std::is_constructible, std::size_t, std::size_t>::value, + "!std::is_constructible, std::size_t, std::size_t>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, std::array&>::value, + "!std::is_constructible, std::array&>"); +static_assert(!std::is_constructible, const std::array&>::value, + "!std::is_constructible, const std::array&>"); + +static_assert(!std::is_constructible, int*, double*>::value, + "!std::is_constructible, int*, double*>"); +static_assert(!std::is_constructible, int (&)[500]>::value, + "!std::is_constructible, int(&)[500]>"); +static_assert(!std::is_constructible, const int*, int*>::value, + "!std::is_constructible, const int*, int*>"); +static_assert(!std::is_constructible, const int*, const int*>::value, + "!std::is_constructible, const int*, const int*>"); +static_assert(!std::is_constructible, const int*, std::size_t>::value, + "!std::is_constructible, const int*, std::size_t>"); +static_assert(!std::is_constructible, const int*, double*>::value, + "!std::is_constructible, const int*, double*>"); +static_assert(!std::is_constructible, const int (&)[3]>::value, + "!std::is_constructible, const int(&)[3]>"); +static_assert(!std::is_constructible, double*, std::size_t>::value, + "!std::is_constructible, double*, std::size_t>"); +static_assert(!std::is_constructible, double*, int*>::value, + "!std::is_constructible, double*, int*>"); +static_assert(!std::is_constructible, double*, const int*>::value, + "!std::is_constructible, double*, const int*>"); +static_assert(!std::is_constructible, double*, double*>::value, + "!std::is_constructible, double*, double*>"); +static_assert(!std::is_constructible, double (&)[3]>::value, + "!std::is_constructible, double(&)[3]>"); + +static_assert(!std::is_constructible, std::size_t, int*>::value, + "!std::is_constructible, std::size_t, int*>"); +static_assert(!std::is_constructible, std::size_t, std::size_t>::value, + "!std::is_constructible, std::size_t, std::size_t>"); +static_assert(!std::is_constructible, std::array&>::value, + "!std::is_constructible, std::array&>"); +static_assert(!std::is_constructible, std::array&>::value, + "!std::is_constructible, std::array&>"); +static_assert(!std::is_constructible, const std::array&>::value, + "!std::is_constructible, const std::array&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); + +static_assert(!std::is_constructible, double (&)[3]>::value, + "!std::is_constructible, double(&)[3]>"); +static_assert(!std::is_constructible, std::array&>::value, + "!std::is_constructible, std::array&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); + +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert( + !std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); + +static_assert(!std::is_constructible, Derived (&)[3]>::value, + "!std::is_constructible, Derived(&)[3]>"); +static_assert(!std::is_constructible, std::array&>::value, + "!std::is_constructible, std::array&>"); +static_assert(!std::is_constructible, std::vector&>::value, + "!std::is_constructible, std::vector&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); + +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, Derived (&)[3]>::value, + "!std::is_constructible, Derived(&)[3]>"); +static_assert(!std::is_constructible, std::array&>::value, + "!std::is_constructible, std::array&>"); + +static_assert(!std::is_constructible, Derived (&)[3]>::value, + "!std::is_constructible, Derived(&)[3]>"); +static_assert(!std::is_constructible, const Derived (&)[3]>::value, + "!std::is_constructible, const Derived(&)[3]>"); +static_assert(!std::is_constructible, std::array&>::value, + "!std::is_constructible, std::array&>"); +static_assert(!std::is_constructible, const std::array&>::value, + "!std::is_constructible, const std::array&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert( + !std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); + +static_assert(!std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); +static_assert( + !std::is_constructible, const gsl::span&>::value, + "!std::is_constructible, const gsl::span&>"); + +static_assert(!std::is_constructible, std::array&>::value, + "!std::is_constructible, std::array&>"); +static_assert(!std::is_constructible, const std::array&>::value, + "!std::is_constructible, const std::array&>"); + +// Explicit construction enabled in P1976R2 +static_assert(std::is_constructible, const gsl::span&>::value, + "std::is_constructible, const gsl::span&>"); +static_assert(std::is_constructible, const gsl::span&>::value, + "std::is_constructible, const gsl::span&>"); +static_assert(std::is_constructible, const gsl::span&>::value, + "std::is_constructible, const gsl::span&>"); + +// no throw copy constructor +static_assert(std::is_nothrow_copy_constructible>::value, + "std::is_nothrow_copy_constructible>"); +static_assert(std::is_nothrow_copy_constructible>::value, + "std::is_nothrow_copy_constructible>"); +static_assert(std::is_nothrow_copy_constructible>::value, + "std::is_nothrow_copy_constructible>"); +static_assert(std::is_nothrow_copy_constructible>::value, + "std::is_nothrow_copy_constructible>"); + +// no throw copy assignment +static_assert(std::is_nothrow_copy_assignable>::value, + "std::is_nothrow_copy_assignable>"); +static_assert(std::is_nothrow_copy_assignable>::value, + "std::is_nothrow_copy_assignable>"); +static_assert(std::is_nothrow_copy_assignable>::value, + "std::is_nothrow_copy_assignable>"); +static_assert(std::is_nothrow_copy_assignable>::value, + "std::is_nothrow_copy_assignable>"); + +// no throw destruction +static_assert(std::is_nothrow_destructible>::value, + "std::is_nothrow_destructible>"); +static_assert(std::is_nothrow_destructible>::value, + "std::is_nothrow_destructible>"); +static_assert(std::is_nothrow_destructible>::value, + "std::is_nothrow_destructible>"); + +// conversions +static_assert(std::is_convertible>::value, + "std::is_convertible>"); +static_assert(std::is_convertible>::value, + "std::is_convertible>"); +static_assert(std::is_convertible>::value, + "std::is_convertible>"); + +static_assert(std::is_convertible>::value, + "std::is_convertible>"); + +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); + +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); + +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); + +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); + +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); + +static_assert(std::is_convertible&, gsl::span>::value, + "std::is_convertible&, gsl::span>"); + +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + +template +static constexpr bool AsWritableBytesCompilesFor = false; + +template +static constexpr bool + AsWritableBytesCompilesFor()))>> = true; + +static_assert(AsWritableBytesCompilesFor>, + "AsWritableBytesCompilesFor>"); +static_assert(AsWritableBytesCompilesFor>, + "AsWritableBytesCompilesFor>"); +static_assert(!AsWritableBytesCompilesFor>, + "!AsWritableBytesCompilesFor>"); +static_assert(!AsWritableBytesCompilesFor>, + "!AsWritableBytesCompilesFor>"); diff --git a/kernel/third_party/GSL-5.0.0/tests/span_ext_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/span_ext_tests.cpp new file mode 100644 index 0000000..57c762a --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/span_ext_tests.cpp @@ -0,0 +1,380 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include + +#include // for span and span_ext +#include // for narrow_cast, at + +#include // for array +#include // for terminate +#include // for cerr +#include // for vector + +using namespace std; +using namespace gsl; + +#include "deathTestCommon.h" + +TEST(span_ext_test, make_span_from_pointer_length_constructor) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. from_pointer_length_constructor"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + int arr[4] = {1, 2, 3, 4}; + + { + auto s = make_span(&arr[0], 2); + EXPECT_TRUE(s.size() == 2); + EXPECT_TRUE(s.data() == &arr[0]); + EXPECT_TRUE(s[0] == 1); + EXPECT_TRUE(s[1] == 2); + } + + { + int* p = nullptr; + auto s = make_span(p, narrow_cast::size_type>(0)); + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + } + + { + int* p = nullptr; + auto workaround_macro = [=]() { make_span(p, 2); }; + EXPECT_DEATH(workaround_macro(), expected); + } +} + +TEST(span_ext_test, make_span_from_pointer_pointer_construction) +{ + int arr[4] = {1, 2, 3, 4}; + + { + auto s = make_span(&arr[0], &arr[2]); + EXPECT_TRUE(s.size() == 2); + EXPECT_TRUE(s.data() == &arr[0]); + EXPECT_TRUE(s[0] == 1); + EXPECT_TRUE(s[1] == 2); + } + + { + auto s = make_span(&arr[0], &arr[0]); + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == &arr[0]); + } + + { + int* p = nullptr; + auto s = make_span(p, p); + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + } +} + +TEST(span_ext_test, make_span_from_array_constructor) +{ + int arr[5] = {1, 2, 3, 4, 5}; + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + int arr3d[2][3][2] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + + { + const auto s = make_span(arr); + EXPECT_TRUE(s.size() == 5); + EXPECT_TRUE(s.data() == std::addressof(arr[0])); + } + + { + const auto s = make_span(std::addressof(arr2d[0]), 1); + EXPECT_TRUE(s.size() == 1); + EXPECT_TRUE(s.data() == std::addressof(arr2d[0])); + } + + { + const auto s = make_span(std::addressof(arr3d[0]), 1); + EXPECT_TRUE(s.size() == 1); + EXPECT_TRUE(s.data() == std::addressof(arr3d[0])); + } +} + +TEST(span_ext_test, make_span_from_dynamic_array_constructor) +{ + double (*arr)[3][4] = new double[100][3][4]; + + { + auto s = make_span(&arr[0][0][0], 10); + EXPECT_TRUE(s.size() == 10); + EXPECT_TRUE(s.data() == &arr[0][0][0]); + } + + delete[] arr; +} + +TEST(span_ext_test, make_span_from_std_array_constructor) +{ + std::array arr = {1, 2, 3, 4}; + + { + auto s = make_span(arr); + EXPECT_TRUE(s.size() == arr.size()); + EXPECT_TRUE(s.data() == arr.data()); + } + + // This test checks for the bug found in gcc 6.1, 6.2, 6.3, 6.4, 6.5 7.1, 7.2, 7.3 - issue #590 + { + gsl::span s1 = make_span(arr); + + static gsl::span s2; + s2 = s1; + +#if defined(__GNUC__) && __GNUC__ == 6 && (__GNUC_MINOR__ == 4 || __GNUC_MINOR__ == 5) && \ + __GNUC_PATCHLEVEL__ == 0 && defined(__OPTIMIZE__) + // Known to be broken in gcc 6.4 and 6.5 with optimizations + // Issue in gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83116 + EXPECT_TRUE(s1.size() == 4); + EXPECT_TRUE(s2.size() == 0); +#else + EXPECT_TRUE(s1.size() == s2.size()); +#endif + } +} + +TEST(span_ext_test, make_span_from_const_std_array_constructor) +{ + const std::array arr = {1, 2, 3, 4}; + + { + auto s = make_span(arr); + EXPECT_TRUE(s.size() == arr.size()); + EXPECT_TRUE(s.data() == arr.data()); + } +} + +TEST(span_ext_test, make_span_from_std_array_const_constructor) +{ + std::array arr = {1, 2, 3, 4}; + + { + auto s = make_span(arr); + EXPECT_TRUE(s.size() == arr.size()); + EXPECT_TRUE(s.data() == arr.data()); + } +} + +TEST(span_ext_test, make_span_from_container_constructor) +{ + std::vector v = {1, 2, 3}; + const std::vector cv = v; + + { + auto s = make_span(v); + EXPECT_TRUE(s.size() == v.size()); + EXPECT_TRUE(s.data() == v.data()); + + auto cs = make_span(cv); + EXPECT_TRUE(cs.size() == cv.size()); + EXPECT_TRUE(cs.data() == cv.data()); + } +} + +TEST(span_test, interop_with_gsl_at) +{ + std::vector vec{1, 2, 3, 4, 5}; + gsl::span sp{vec}; + + std::vector cvec{1, 2, 3, 4, 5}; + gsl::span csp{cvec}; + + for (gsl::index i = 0; i < gsl::narrow_cast(vec.size()); ++i) + { + EXPECT_TRUE(&gsl::at(sp, i) == &vec[gsl::narrow_cast(i)]); + EXPECT_TRUE(&gsl::at(csp, i) == &cvec[gsl::narrow_cast(i)]); + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. interop_with_gsl_at"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + EXPECT_DEATH(gsl::at(sp, -1), expected); + EXPECT_DEATH(gsl::at(sp, gsl::narrow_cast(sp.size())), expected); + EXPECT_DEATH(gsl::at(csp, -1), expected); + EXPECT_DEATH(gsl::at(csp, gsl::narrow_cast(sp.size())), expected); +} + +TEST(span_ext_test, iterator_free_functions) +{ + int a[] = {1, 2, 3, 4}; + gsl::span s{a}; + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + + EXPECT_TRUE(s.begin() == begin(s)); + EXPECT_TRUE(s.end() == end(s)); + + EXPECT_TRUE(s.rbegin() == rbegin(s)); + EXPECT_TRUE(s.rend() == rend(s)); + + EXPECT_TRUE(s.begin() == cbegin(s)); + EXPECT_TRUE(s.end() == cend(s)); + + EXPECT_TRUE(s.rbegin() == crbegin(s)); + EXPECT_TRUE(s.rend() == crend(s)); +} + +TEST(span_ext_test, ssize_free_function) +{ + int a[] = {1, 2, 3, 4}; + gsl::span s{a}; + + EXPECT_FALSE((std::is_same::value)); + EXPECT_TRUE(s.size() == static_cast(ssize(s))); +} + +#ifndef GSL_KERNEL_MODE +TEST(span_ext_test, comparison_operators) +{ + { + gsl::span s1; + gsl::span s2; + EXPECT_TRUE(s1 == s2); + EXPECT_FALSE(s1 != s2); + EXPECT_FALSE(s1 < s2); + EXPECT_TRUE(s1 <= s2); + EXPECT_FALSE(s1 > s2); + EXPECT_TRUE(s1 >= s2); + EXPECT_TRUE(s2 == s1); + EXPECT_FALSE(s2 != s1); + EXPECT_FALSE(s2 != s1); + EXPECT_TRUE(s2 <= s1); + EXPECT_FALSE(s2 > s1); + EXPECT_TRUE(s2 >= s1); + } + + { + int arr[] = {2, 1}; + gsl::span s1 = arr; + gsl::span s2 = arr; + + EXPECT_TRUE(s1 == s2); + EXPECT_FALSE(s1 != s2); + EXPECT_FALSE(s1 < s2); + EXPECT_TRUE(s1 <= s2); + EXPECT_FALSE(s1 > s2); + EXPECT_TRUE(s1 >= s2); + EXPECT_TRUE(s2 == s1); + EXPECT_FALSE(s2 != s1); + EXPECT_FALSE(s2 < s1); + EXPECT_TRUE(s2 <= s1); + EXPECT_FALSE(s2 > s1); + EXPECT_TRUE(s2 >= s1); + } + + { + int arr[] = {2, 1}; // bigger + + gsl::span s1; + gsl::span s2 = arr; + + EXPECT_TRUE(s1 != s2); + EXPECT_TRUE(s2 != s1); + EXPECT_FALSE(s1 == s2); + EXPECT_FALSE(s2 == s1); + EXPECT_TRUE(s1 < s2); + EXPECT_FALSE(s2 < s1); + EXPECT_TRUE(s1 <= s2); + EXPECT_FALSE(s2 <= s1); + EXPECT_TRUE(s2 > s1); + EXPECT_FALSE(s1 > s2); + EXPECT_TRUE(s2 >= s1); + EXPECT_FALSE(s1 >= s2); + } + + { + int arr1[] = {1, 2}; + int arr2[] = {1, 2}; + gsl::span s1 = arr1; + gsl::span s2 = arr2; + + EXPECT_TRUE(s1 == s2); + EXPECT_FALSE(s1 != s2); + EXPECT_FALSE(s1 < s2); + EXPECT_TRUE(s1 <= s2); + EXPECT_FALSE(s1 > s2); + EXPECT_TRUE(s1 >= s2); + EXPECT_TRUE(s2 == s1); + EXPECT_FALSE(s2 != s1); + EXPECT_FALSE(s2 < s1); + EXPECT_TRUE(s2 <= s1); + EXPECT_FALSE(s2 > s1); + EXPECT_TRUE(s2 >= s1); + } + + { + int arr[] = {1, 2, 3}; + + gsl::span s1 = {&arr[0], 2}; // shorter + gsl::span s2 = arr; // longer + + EXPECT_TRUE(s1 != s2); + EXPECT_TRUE(s2 != s1); + EXPECT_FALSE(s1 == s2); + EXPECT_FALSE(s2 == s1); + EXPECT_TRUE(s1 < s2); + EXPECT_FALSE(s2 < s1); + EXPECT_TRUE(s1 <= s2); + EXPECT_FALSE(s2 <= s1); + EXPECT_TRUE(s2 > s1); + EXPECT_FALSE(s1 > s2); + EXPECT_TRUE(s2 >= s1); + EXPECT_FALSE(s1 >= s2); + } + + { + int arr1[] = {1, 2}; // smaller + int arr2[] = {2, 1}; // bigger + + gsl::span s1 = arr1; + gsl::span s2 = arr2; + + EXPECT_TRUE(s1 != s2); + EXPECT_TRUE(s2 != s1); + EXPECT_FALSE(s1 == s2); + EXPECT_FALSE(s2 == s1); + EXPECT_TRUE(s1 < s2); + EXPECT_FALSE(s2 < s1); + EXPECT_TRUE(s1 <= s2); + EXPECT_FALSE(s2 <= s1); + EXPECT_TRUE(s2 > s1); + EXPECT_FALSE(s1 > s2); + EXPECT_TRUE(s2 >= s1); + EXPECT_FALSE(s1 >= s2); + } +} +#endif // GSL_KERNEL_MODE diff --git a/kernel/third_party/GSL-5.0.0/tests/span_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/span_tests.cpp new file mode 100644 index 0000000..2c7afa9 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/span_tests.cpp @@ -0,0 +1,1428 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include + +#include // for byte +#include // for span, span_iterator, operator==, operator!= +#include // for narrow_cast, at + +#include // for array +#include // for ptrdiff_t +#include // for ptrdiff_t +#include // for reverse_iterator, operator-, operator== +#include // for unique_ptr, shared_ptr, make_unique, allo... +#include // for match_results, sub_match, match_results<>... +#include // for string +#include // for integral_constant<>::value, is_default_co... +#include +#include // for vector + +// the string_view include and macro are used in the deduction guide verification +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) +#ifdef __has_include +#if __has_include() +#include +#define HAS_STRING_VIEW +#endif // __has_include() +#endif // __has_include +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) +#if defined(__cplusplus) && __cplusplus >= 202002L +#include +#endif // __cplusplus >= 202002L + +#include "deathTestCommon.h" + +using namespace gsl; + +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + +namespace +{ + +struct BaseClass +{ +}; +struct DerivedClass : BaseClass +{ +}; +struct AddressOverloaded +{ +#if (__cplusplus > 201402L) + [[maybe_unused]] +#endif + AddressOverloaded operator&() const + { + return {}; + } +}; +} // namespace + +TEST(span_test, constructors) +{ + span s; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + + span cs; + EXPECT_TRUE(cs.size() == 0); + EXPECT_TRUE(cs.data() == nullptr); +} + +TEST(span_test, constructors_with_extent) +{ + span s; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + + span cs; + EXPECT_TRUE(cs.size() == 0); + EXPECT_TRUE(cs.data() == nullptr); +} + +TEST(span_test, constructors_with_bracket_init) +{ + span s{}; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + + span cs{}; + EXPECT_TRUE(cs.size() == 0); + EXPECT_TRUE(cs.data() == nullptr); +} + +TEST(span_test, size_optimization) +{ + span s; + EXPECT_TRUE(sizeof(s) == sizeof(int*) + sizeof(ptrdiff_t)); + + span se; + EXPECT_TRUE(sizeof(se) == sizeof(int*)); +} + +TEST(span_test, from_nullptr_size_constructor) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. from_nullptr_size_constructor"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + { + span s{nullptr, narrow_cast::size_type>(0)}; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + + span cs{nullptr, narrow_cast::size_type>(0)}; + EXPECT_TRUE(cs.size() == 0); + EXPECT_TRUE(cs.data() == nullptr); + } + { + auto workaround_macro = []() { + const span s{nullptr, narrow_cast::size_type>(0)}; + }; + EXPECT_DEATH(workaround_macro(), expected); + } + { + auto workaround_macro = []() { const span s{nullptr, 1}; }; + EXPECT_DEATH(workaround_macro(), expected); + + auto const_workaround_macro = []() { const span s{nullptr, 1}; }; + EXPECT_DEATH(const_workaround_macro(), expected); + } + { + auto workaround_macro = []() { const span s{nullptr, 1}; }; + EXPECT_DEATH(workaround_macro(), expected); + + auto const_workaround_macro = []() { const span s{nullptr, 1}; }; + EXPECT_DEATH(const_workaround_macro(), expected); + } + { + span s{nullptr, narrow_cast::size_type>(0)}; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + + span cs{nullptr, narrow_cast::size_type>(0)}; + EXPECT_TRUE(cs.size() == 0); + EXPECT_TRUE(cs.data() == nullptr); + } +} + +TEST(span_test, from_pointer_length_constructor) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. from_pointer_length_constructor"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + int arr[4] = {1, 2, 3, 4}; + + { + for (int i = 0; i < 4; ++i) + { + { + span s = {&arr[0], narrow_cast(i)}; + EXPECT_TRUE(s.size() == narrow_cast(i)); + EXPECT_TRUE(s.data() == &arr[0]); + EXPECT_TRUE(s.empty() == (i == 0)); + for (int j = 0; j < i; ++j) EXPECT_TRUE(arr[j] == s[narrow_cast(j)]); + } + { + span s = {&arr[i], 4 - narrow_cast(i)}; + EXPECT_TRUE(s.size() == 4 - narrow_cast(i)); + EXPECT_TRUE(s.data() == &arr[i]); + EXPECT_TRUE(s.empty() == ((4 - i) == 0)); + + for (int j = 0; j < 4 - i; ++j) + EXPECT_TRUE(arr[j + i] == s[narrow_cast(j)]); + } + } + } + + { + span s{&arr[0], 2}; + EXPECT_TRUE(s.size() == 2); + EXPECT_TRUE(s.data() == &arr[0]); + EXPECT_TRUE(s[0] == 1); + EXPECT_TRUE(s[1] == 2); + } + + { + int* p = nullptr; + span s{p, narrow_cast::size_type>(0)}; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + } + + { + int* p = nullptr; + auto workaround_macro = [=]() { const span s{p, 2}; }; + EXPECT_DEATH(workaround_macro(), expected); + } +} + +TEST(span_test, from_pointer_pointer_construction) +{ + // const auto terminateHandler = std::set_terminate([] { + // std::cerr << "Expected Death. from_pointer_pointer_construction"; + // std::abort(); + // }); + // const auto expected = GetExpectedDeathString(terminateHandler); + + int arr[4] = {1, 2, 3, 4}; + + { + span s{&arr[0], &arr[2]}; + EXPECT_TRUE(s.size() == 2); + EXPECT_TRUE(s.data() == &arr[0]); + EXPECT_TRUE(s[0] == 1); + EXPECT_TRUE(s[1] == 2); + } + { + span s{&arr[0], &arr[2]}; + EXPECT_TRUE(s.size() == 2); + EXPECT_TRUE(s.data() == &arr[0]); + EXPECT_TRUE(s[0] == 1); + EXPECT_TRUE(s[1] == 2); + } + + { + span s{&arr[0], &arr[0]}; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == &arr[0]); + } + + { + span s{&arr[0], &arr[0]}; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == &arr[0]); + } + + // this test succeeds on all platforms, gsl::span is more relaxed than std::span where this + // would be UB + //{ + // auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; + // EXPECT_DEATH(workaround_macro(), expected); + //} + + { + int* p = nullptr; + span s{p, p}; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + } + + { + int* p = nullptr; + span s{p, p}; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + } +} + +template +static constexpr bool CtorCompilesFor = false; +template +static constexpr bool CtorCompilesFor()})>> = true; + +TEST(span_test, from_array_constructor) +{ + int arr[5] = {1, 2, 3, 4, 5}; + + static_assert(!CtorCompilesFor, int[5]>, "!CtorCompilesFor, int[5]>"); + static_assert(!CtorCompilesFor, int[5]>, "!CtorCompilesFor, int[5]>"); + static_assert(!CtorCompilesFor, int[2][3]>, "!CtorCompilesFor, int[2][3]>"); + + { + const span s{arr}; + EXPECT_TRUE(s.size() == 5); + EXPECT_TRUE(s.data() == &arr[0]); + } + + { + const span s{arr}; + EXPECT_TRUE(s.size() == 5); + EXPECT_TRUE(s.data() == &arr[0]); + } + + int arr2d[2][3] = {1, 2, 3, 4, 5, 6}; + + static_assert(!CtorCompilesFor, int[2][3]>, + "!CtorCompilesFor, int[2][3]>"); + static_assert(!CtorCompilesFor, int[2][3]>, + "!CtorCompilesFor, int[2][3]>"); + + { + const span s{std::addressof(arr2d[0]), 1}; + EXPECT_TRUE(s.size() == 1); + EXPECT_TRUE(s.data() == std::addressof(arr2d[0])); + } + + int arr3d[2][3][2] = {{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}}; + + static_assert(!CtorCompilesFor, int[2][3][2]>, + "!CtorCompilesFor, int[2][3][2]>"); + static_assert(!CtorCompilesFor, int[2][3][2]>, + "!CtorCompilesFor, int[2][3][2]>"); + static_assert(!CtorCompilesFor, int[2][3][2]>, + "!CtorCompilesFor, int[2][3][2]>"); + static_assert(!CtorCompilesFor, int[2][3][2]>, + "!CtorCompilesFor, int[2][3][2]>"); + + { + const span s{std::addressof(arr3d[0]), 1}; + EXPECT_TRUE(s.size() == 1); + } + + AddressOverloaded ao_arr[5] = {}; + + { + const span s{ao_arr}; + EXPECT_TRUE(s.size() == 5); + EXPECT_TRUE(s.data() == std::addressof(ao_arr[0])); + } +} + +TEST(span_test, from_dynamic_array_constructor) +{ + double (*arr)[3][4] = new double[100][3][4]; + + { + span s(&arr[0][0][0], 10); + EXPECT_TRUE(s.size() == 10); + EXPECT_TRUE(s.data() == &arr[0][0][0]); + } + + delete[] arr; +} + +template +static constexpr bool ConversionCompilesFor = false; +template +static constexpr bool + ConversionCompilesFor()(std::declval()))>> = + true; + +TEST(span_test, from_std_array_constructor) +{ + std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + EXPECT_TRUE(s.size() == arr.size()); + EXPECT_TRUE(s.data() == arr.data()); + + span cs{arr}; + EXPECT_TRUE(cs.size() == arr.size()); + EXPECT_TRUE(cs.data() == arr.data()); + } + + { + span s{arr}; + EXPECT_TRUE(s.size() == arr.size()); + EXPECT_TRUE(s.data() == arr.data()); + + span cs{arr}; + EXPECT_TRUE(cs.size() == arr.size()); + EXPECT_TRUE(cs.data() == arr.data()); + } + + { + std::array empty_arr{}; + span s{empty_arr}; + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.empty()); + } + + std::array ao_arr{}; + + { + span fs{ao_arr}; + EXPECT_TRUE(fs.size() == ao_arr.size()); + EXPECT_TRUE(ao_arr.data() == fs.data()); + } + + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + +#if !defined(_MSC_VER) || (__cplusplus >= 201703L) + // Fails on MSVC. TODO: report a feedback bug. + static_assert(!ConversionCompilesFor, std::array>, + "!ConversionCompilesFor, std::array>"); +#endif + + { + auto get_an_array = []() -> std::array { return {1, 2, 3, 4}; }; + auto take_a_span = [](span) {}; + // try to take a temporary std::array + static_assert(ConversionCompilesFor, std::array>, + "ConversionCompilesFor, std::array>"); + take_a_span(get_an_array()); + } +} + +TEST(span_test, from_const_std_array_constructor) +{ + const std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + EXPECT_TRUE(s.size() == arr.size()); + EXPECT_TRUE(s.data() == arr.data()); + } + + { + span s{arr}; + EXPECT_TRUE(s.size() == arr.size()); + EXPECT_TRUE(s.data() == arr.data()); + } + + const std::array ao_arr{}; + + { + span s{ao_arr}; + EXPECT_TRUE(s.size() == ao_arr.size()); + EXPECT_TRUE(s.data() == ao_arr.data()); + } + + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + static_assert(!CtorCompilesFor, std::array&>, + "!CtorCompilesFor, std::array&>"); + + { + auto get_an_array = []() -> const std::array { return {1, 2, 3, 4}; }; + auto take_a_span = [](span s) { static_cast(s); }; + // try to take a temporary std::array + take_a_span(get_an_array()); + } +} + +TEST(span_test, from_std_array_const_constructor) +{ + std::array arr = {1, 2, 3, 4}; + + { + span s{arr}; + EXPECT_TRUE(s.size() == arr.size()); + EXPECT_TRUE(s.data() == arr.data()); + } + + { + span s{arr}; + EXPECT_TRUE(s.size() == arr.size()); + EXPECT_TRUE(s.data() == arr.data()); + } + + static_assert(!CtorCompilesFor, const std::array&>, + "!CtorCompilesFor, const std::array&>"); + static_assert(!CtorCompilesFor, const std::array&>, + "!CtorCompilesFor, const std::array&>"); + static_assert(!CtorCompilesFor, const std::array&>, + "!CtorCompilesFor, const std::array&>"); + static_assert(!CtorCompilesFor, const std::array&>, + "!CtorCompilesFor, const std::array&>"); +} + +TEST(span_test, from_container_constructor) +{ + std::vector v = {1, 2, 3}; + const std::vector cv = v; + + { + span s{v}; + EXPECT_TRUE(s.size() == v.size()); + EXPECT_TRUE(s.data() == v.data()); + + span cs{v}; + EXPECT_TRUE(cs.size() == v.size()); + EXPECT_TRUE(cs.data() == v.data()); + } + + std::string str = "hello"; + const std::string cstr = "hello"; + + { + static_assert(CtorCompilesFor, std::string&> == (__cplusplus >= 201703L), + "CtorCompilesFor, std::string&> == (__cplusplus >= 201703L)"); + + span cs{str}; + EXPECT_TRUE(cs.size() == str.size()); + EXPECT_TRUE(cs.data() == str.data()); + } + + { + static_assert(!CtorCompilesFor, const std::string&>, + "!CtorCompilesFor, const std::string&>"); + + span cs{cstr}; + EXPECT_TRUE(cs.size() == cstr.size()); + EXPECT_TRUE(cs.data() == cstr.data()); + } + +#if !defined(_MSC_VER) || (__cplusplus >= 201703L) + // Fails on MSVC. TODO: report a feedback bug. + static_assert(!ConversionCompilesFor, std::vector>, + "!ConversionCompilesFor, std::vector>"); +#endif // !defined(_MSC_VER) || (_MSC_VER > 1942) || (__cplusplus >= 201703L) + + { + auto get_temp_vector = []() -> std::vector { return {}; }; + auto use_span = [](span s) { static_cast(s); }; + use_span(get_temp_vector()); + } + + static_assert(!ConversionCompilesFor, std::string>, + "!ConversionCompilesFor, std::string>"); + + { + auto get_temp_string = []() -> std::string { return {}; }; + auto use_span = [](span s) { static_cast(s); }; + use_span(get_temp_string()); + } + + static_assert(!ConversionCompilesFor, const std::vector>, + "!ConversionCompilesFor, const std::vector>"); + static_assert(!ConversionCompilesFor, const std::string>, + "!ConversionCompilesFor, const std::string>"); + + { + auto get_temp_string = []() -> const std::string { return {}; }; + auto use_span = [](span s) { static_cast(s); }; + use_span(get_temp_string()); + } + + static_assert(!CtorCompilesFor, std::map&>, + "!CtorCompilesFor, std::map&>"); +} + +TEST(span_test, from_convertible_span_constructor) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. from_convertible_span_constructor"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + { + span avd; + span avcd = avd; + static_cast(avcd); + } + + { + std::array arr{}; + span avd{arr}; + span avcd = avd; + static_cast(avcd); + } + + { + std::array arr{}; + span avd{arr}; + span avcd = avd; + static_cast(avcd); + } + + { + std::array arr{}; + span avd{arr}; + span avcd{avd}; + static_cast(avcd); + } + + { + std::array arr{}; + span avd{arr}; + using T = span; + EXPECT_DEATH(T{avd}, expected); + } + + { + std::array arr{}; + span avd{arr}; + using T = span; + EXPECT_DEATH(T{avd}, expected); + } + + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); + static_assert(!ConversionCompilesFor, span&>, + "!ConversionCompilesFor, span&>"); +} + +TEST(span_test, copy_move_and_assignment) +{ + span s1; + EXPECT_TRUE(s1.empty()); + + int arr[] = {3, 4, 5}; + + span s2 = arr; + EXPECT_TRUE(s2.size() == 3); + EXPECT_TRUE(s2.data() == &arr[0]); + + s2 = s1; + EXPECT_TRUE(s2.empty()); + + auto get_temp_span = [&]() -> span { return {&arr[1], 2}; }; + auto use_span = [&](span s) { + EXPECT_TRUE(s.size() == 2); + EXPECT_TRUE(s.data() == &arr[1]); + }; + use_span(get_temp_span()); + + s1 = get_temp_span(); + EXPECT_TRUE(s1.size() == 2); + EXPECT_TRUE(s1.data() == &arr[1]); +} + +TEST(span_test, first) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. first"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + EXPECT_TRUE(av.first<2>().size() == 2); + EXPECT_TRUE(av.first(2).size() == 2); + } + + { + span av = arr; + EXPECT_TRUE(av.first<0>().size() == 0); + EXPECT_TRUE(av.first(0).size() == 0); + } + + { + span av = arr; + EXPECT_TRUE(av.first<5>().size() == 5); + EXPECT_TRUE(av.first(5).size() == 5); + } + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + (void) av.first<6>(); +#endif + EXPECT_DEATH(av.first(6), expected); + } + + { + span av; + EXPECT_TRUE(av.first<0>().size() == 0); + EXPECT_TRUE(av.first(0).size() == 0); + } +} + +TEST(span_test, last) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. last"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + EXPECT_TRUE(av.last<2>().size() == 2); + EXPECT_TRUE(av.last(2).size() == 2); + } + + { + span av = arr; + EXPECT_TRUE(av.last<0>().size() == 0); + EXPECT_TRUE(av.last(0).size() == 0); + } + + { + span av = arr; + EXPECT_TRUE(av.last<5>().size() == 5); + EXPECT_TRUE(av.last(5).size() == 5); + } + + { + span av = arr; +#ifdef CONFIRM_COMPILATION_ERRORS + (void) av.last<6>(); +#endif + EXPECT_DEATH(av.last(6), expected); + } + + { + span av; + EXPECT_TRUE(av.last<0>().size() == 0); + EXPECT_TRUE(av.last(0).size() == 0); + } +} + +TEST(span_test, subspan) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. subspan"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + int arr[5] = {1, 2, 3, 4, 5}; + + { + span av = arr; + EXPECT_TRUE((av.subspan<2, 2>().size()) == 2); + EXPECT_TRUE(decltype(av.subspan<2, 2>())::extent == 2); + EXPECT_TRUE(av.subspan(2, 2).size() == 2); + + EXPECT_TRUE((av.subspan<2, 3>().size()) == 3); + EXPECT_TRUE(decltype(av.subspan<2, 3>())::extent == 3); + EXPECT_TRUE(av.subspan(2, 3).size() == 3); + } + + { + span av = arr; + EXPECT_TRUE((av.subspan<0, 0>().size()) == 0); + EXPECT_TRUE(decltype(av.subspan<0, 0>())::extent == 0); + EXPECT_TRUE(av.subspan(0, 0).size() == 0); + } + + { + span av = arr; + EXPECT_TRUE((av.subspan<0, 5>().size()) == 5); + EXPECT_TRUE(decltype(av.subspan<0, 5>())::extent == 5); + EXPECT_TRUE(av.subspan(0, 5).size() == 5); + +#ifdef CONFIRM_COMPILATION_ERRORS + (void) av.subspan<0, 6>(); + (void) av.subspan<1, 5>(); +#endif + EXPECT_DEATH(av.subspan(0, 6), expected); + EXPECT_DEATH(av.subspan(1, 5), expected); + } + + { + span av = arr; + EXPECT_TRUE((av.subspan<4, 0>().size()) == 0); + EXPECT_TRUE(decltype(av.subspan<4, 0>())::extent == 0); + EXPECT_TRUE(av.subspan(4, 0).size() == 0); + + EXPECT_TRUE((av.subspan<5, 0>().size()) == 0); + EXPECT_TRUE(decltype(av.subspan<5, 0>())::extent == 0); + EXPECT_TRUE(av.subspan(5, 0).size() == 0); + +#ifdef CONFIRM_COMPILATION_ERRORS + (void) av.subspan<6, 0>(); +#endif + EXPECT_DEATH(av.subspan(6, 0), expected); + } + + { + span av = arr; + EXPECT_TRUE(av.subspan<1>().size() == 4); + EXPECT_TRUE(decltype(av.subspan<1>())::extent == 4); + EXPECT_TRUE(av.subspan(1).size() == 4); + } + + { + span av; + EXPECT_TRUE((av.subspan<0, 0>().size()) == 0); + EXPECT_TRUE(decltype(av.subspan<0, 0>())::extent == 0); + EXPECT_TRUE(av.subspan(0, 0).size() == 0); + + EXPECT_DEATH((av.subspan<1, 0>()), expected); + EXPECT_DEATH((av.subspan(1, 0)), expected); + } + + { + span av; + EXPECT_TRUE((av.subspan<0>().size()) == 0); + EXPECT_TRUE(decltype(av.subspan<0>())::extent == dynamic_extent); + EXPECT_TRUE(av.subspan(0).size() == 0); + + EXPECT_DEATH(av.subspan<1>(), expected); + EXPECT_TRUE(decltype(av.subspan<1>())::extent == dynamic_extent); + EXPECT_DEATH(av.subspan(1), expected); + } + + { + span av = arr; + EXPECT_TRUE(av.subspan(0).size() == 5); + EXPECT_TRUE(av.subspan<0>().size() == 5); + EXPECT_TRUE(av.subspan(1).size() == 4); + EXPECT_TRUE(av.subspan<1>().size() == 4); + EXPECT_TRUE(av.subspan(4).size() == 1); + EXPECT_TRUE(av.subspan<4>().size() == 1); + EXPECT_TRUE(av.subspan(5).size() == 0); + EXPECT_TRUE(av.subspan<5>().size() == 0); + EXPECT_DEATH(av.subspan(6), expected); + EXPECT_DEATH(av.subspan<6>(), expected); + const auto av2 = av.subspan(1); + for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av2[i] == static_cast(i) + 2); + const auto av3 = av.subspan<1>(); + for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av3[i] == static_cast(i) + 2); + } + + { + span av = arr; + EXPECT_TRUE(av.subspan(0).size() == 5); + EXPECT_TRUE(av.subspan<0>().size() == 5); + EXPECT_TRUE(av.subspan(1).size() == 4); + EXPECT_TRUE(av.subspan<1>().size() == 4); + EXPECT_TRUE(av.subspan(4).size() == 1); + EXPECT_TRUE(av.subspan<4>().size() == 1); + EXPECT_TRUE(av.subspan(5).size() == 0); + EXPECT_TRUE(av.subspan<5>().size() == 0); + EXPECT_DEATH(av.subspan(6), expected); +#ifdef CONFIRM_COMPILATION_ERRORS + EXPECT_DEATH(av.subspan<6>(), expected); +#endif + const auto av2 = av.subspan(1); + for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av2[i] == static_cast(i) + 2); + const auto av3 = av.subspan<1>(); + for (std::size_t i = 0; i < 4; ++i) EXPECT_TRUE(av3[i] == static_cast(i) + 2); + } +} + +TEST(span_test, iterator_default_init) +{ + span::iterator it1; + span::iterator it2; + EXPECT_TRUE(it1 == it2); +} + +TEST(span_test, iterator_comparisons) +{ + int a[] = {1, 2, 3, 4}; + { + span s = a; + span::iterator it = s.begin(); + auto it2 = it + 1; + + EXPECT_TRUE(it == it); + EXPECT_TRUE(it == s.begin()); + EXPECT_TRUE(s.begin() == it); + + EXPECT_TRUE(it != it2); + EXPECT_TRUE(it2 != it); + EXPECT_TRUE(it != s.end()); + EXPECT_TRUE(it2 != s.end()); + EXPECT_TRUE(s.end() != it); + + EXPECT_TRUE(it < it2); + EXPECT_TRUE(it <= it2); + EXPECT_TRUE(it2 <= s.end()); + EXPECT_TRUE(it < s.end()); + + EXPECT_TRUE(it2 > it); + EXPECT_TRUE(it2 >= it); + EXPECT_TRUE(s.end() > it2); + EXPECT_TRUE(s.end() >= it2); + } +} + +TEST(span_test, incomparable_iterators) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. incomparable_iterators"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + int a[] = {1, 2, 3, 4}; + int b[] = {1, 2, 3, 4}; + { + span s = a; + span s2 = b; +#if (__cplusplus > 201402L) + EXPECT_DEATH([[maybe_unused]] bool _ = (s.begin() == s2.begin()), expected); + EXPECT_DEATH([[maybe_unused]] bool _ = (s.begin() <= s2.begin()), expected); +#else + EXPECT_DEATH(bool _ = (s.begin() == s2.begin()), expected); + EXPECT_DEATH(bool _ = (s.begin() <= s2.begin()), expected); +#endif + } +} + +TEST(span_test, begin_end) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. begin_end"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + { + int a[] = {1, 2, 3, 4}; + span s = a; + + span::iterator it = s.begin(); + span::iterator it2 = std::begin(s); + EXPECT_TRUE(it == it2); + + it = s.end(); + it2 = std::end(s); + EXPECT_TRUE(it == it2); + } + + { + int a[] = {1, 2, 3, 4}; + span s = a; + + auto it = s.begin(); + auto first = it; + EXPECT_TRUE(it == first); + EXPECT_TRUE(*it == 1); + + auto beyond = s.end(); + EXPECT_TRUE(it != beyond); + EXPECT_DEATH(*beyond, expected); + + EXPECT_TRUE(beyond - first == 4); + EXPECT_TRUE(first - first == 0); + EXPECT_TRUE(beyond - beyond == 0); + + ++it; + EXPECT_TRUE(it - first == 1); + EXPECT_TRUE(*it == 2); + *it = 22; + EXPECT_TRUE(*it == 22); + EXPECT_TRUE(beyond - it == 3); + + it = first; + EXPECT_TRUE(it == first); + while (it != s.end()) + { + *it = 5; + ++it; + } + + EXPECT_TRUE(it == beyond); + EXPECT_TRUE(it - beyond == 0); + + for (const auto& n : s) { EXPECT_TRUE(n == 5); } + } +} + +TEST(span_test, rbegin_rend) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. rbegin_rend"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + { + int a[] = {1, 2, 3, 4}; + span s = a; + + auto it = s.rbegin(); + auto first = it; + EXPECT_TRUE(it == first); + EXPECT_TRUE(*it == 4); + + auto beyond = s.rend(); + EXPECT_TRUE(it != beyond); +#if (__cplusplus > 201402L) + EXPECT_DEATH([[maybe_unused]] auto _ = *beyond, expected); +#else + EXPECT_DEATH(auto _ = *beyond, expected); +#endif + + EXPECT_TRUE(beyond - first == 4); + EXPECT_TRUE(first - first == 0); + EXPECT_TRUE(beyond - beyond == 0); + + ++it; + EXPECT_TRUE(it - s.rbegin() == 1); + EXPECT_TRUE(*it == 3); + *it = 22; + EXPECT_TRUE(*it == 22); + EXPECT_TRUE(beyond - it == 3); + + it = first; + EXPECT_TRUE(it == first); + while (it != s.rend()) + { + *it = 5; + ++it; + } + + EXPECT_TRUE(it == beyond); + EXPECT_TRUE(it - beyond == 0); + + for (const auto& n : s) { EXPECT_TRUE(n == 5); } + } +} + +template +static constexpr bool AsWritableBytesCompilesFor = false; +template +static constexpr bool + AsWritableBytesCompilesFor()))>> = true; + +TEST(span_test, as_bytes) +{ + int a[] = {1, 2, 3, 4}; + + static_assert(AsWritableBytesCompilesFor>, "AsWriteableBytesCompilesFor>"); + // you should not be able to get writeable bytes for const objects + static_assert(!AsWritableBytesCompilesFor>, + "!AsWriteableBytesCompilesFor>"); + + { + const span s = a; + EXPECT_TRUE(s.size() == 4); + const span bs = as_bytes(s); + EXPECT_TRUE(static_cast(bs.data()) == static_cast(s.data())); + EXPECT_TRUE(bs.size() == s.size_bytes()); + } + + { + span s; + const auto bs = as_bytes(s); + EXPECT_TRUE(bs.size() == s.size()); + EXPECT_TRUE(bs.size() == 0); + EXPECT_TRUE(bs.size_bytes() == 0); + EXPECT_TRUE(static_cast(bs.data()) == static_cast(s.data())); + EXPECT_TRUE(bs.data() == nullptr); + } + + { + span s = a; + const auto bs = as_bytes(s); + EXPECT_TRUE(static_cast(bs.data()) == static_cast(s.data())); + EXPECT_TRUE(bs.size() == s.size_bytes()); + } +} + +TEST(span_test, as_writable_bytes) +{ + int a[] = {1, 2, 3, 4}; + + { + span s; + const auto bs = as_writable_bytes(s); + EXPECT_TRUE(bs.size() == s.size()); + EXPECT_TRUE(bs.size() == 0); + EXPECT_TRUE(bs.size_bytes() == 0); + EXPECT_TRUE(static_cast(bs.data()) == static_cast(s.data())); + EXPECT_TRUE(bs.data() == nullptr); + } + + { + span s = a; + const auto bs = as_writable_bytes(s); + EXPECT_TRUE(static_cast(bs.data()) == static_cast(s.data())); + EXPECT_TRUE(bs.size() == s.size_bytes()); + } +} + +TEST(span_test, fixed_size_conversions) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. fixed_size_conversions"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + int arr[] = {1, 2, 3, 4}; + + // converting to an span from an equal size array is ok + span s4 = arr; + EXPECT_TRUE(s4.size() == 4); + + // converting to dynamic_range is always ok + { + span s = s4; + EXPECT_TRUE(s.size() == s4.size()); + static_cast(s); + } + + // initialization or assignment to static span that REDUCES size is NOT ok + static_assert(!ConversionCompilesFor, int[4]>, + "!ConversionCompilesFor, int[4]>"); + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); + + // even when done dynamically + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); + + // but doing so explicitly is ok + + // you can convert statically + { + const span s2{&arr[0], 2}; + static_cast(s2); + } + { + const span s1 = s4.first<1>(); + static_cast(s1); + } + + // this is not a legal operation in std::span, so we are no longer supporting it + // conversion from span to span via call to `first` + // then convert from span to span + // The dynamic to fixed extents are not supported in the standard + // to make this work, span would need to be span. + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); + + // initialization or assignment to static span that requires size INCREASE is not ok. + int arr2[2] = {1, 2}; + + static_assert(!ConversionCompilesFor, int[2]>, + "!ConversionCompilesFor, int[2]>"); + static_assert(!ConversionCompilesFor, int[2]>, + "!ConversionCompilesFor, int[2]>"); + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); + + { + auto f = [&]() { + const span _s4{arr2, 2}; + static_cast(_s4); + }; + EXPECT_DEATH(f(), expected); + } + + // This no longer compiles. There is no suitable conversion from dynamic span to a fixed size + // span. + // this should fail - we are trying to assign a small dynamic span to a fixed_size larger one + static_assert(!ConversionCompilesFor, span>, + "!ConversionCompilesFor, span>"); +} + +TEST(span_test, interop_with_std_regex) +{ + char lat[] = {'1', '2', '3', '4', '5', '6', 'E', 'F', 'G'}; + span s = lat; + const auto f_it = s.begin() + 7; + + std::match_results::iterator> match; + + std::regex_match(s.begin(), s.end(), match, std::regex(".*")); + EXPECT_TRUE(match.ready()); + EXPECT_FALSE(match.empty()); + EXPECT_TRUE(match[0].matched); + EXPECT_TRUE(match[0].first == s.begin()); + EXPECT_TRUE(match[0].second == s.end()); + + std::regex_search(s.begin(), s.end(), match, std::regex("F")); + EXPECT_TRUE(match.ready()); + EXPECT_FALSE(match.empty()); + EXPECT_TRUE(match[0].matched); + EXPECT_TRUE(match[0].first == f_it); + EXPECT_TRUE(match[0].second == (f_it + 1)); +} + +TEST(span_test, default_constructible) +{ + EXPECT_TRUE((std::is_default_constructible>::value)); + EXPECT_TRUE((std::is_default_constructible>::value)); + EXPECT_FALSE((std::is_default_constructible>::value)); +} + +TEST(span_test, std_container_ctad) +{ +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) + // this test is just to verify that these compile + { + std::vector v{1, 2, 3, 4}; + gsl::span sp{v}; + static_assert(std::is_same>::value); + } + { + std::string str{"foo"}; + gsl::span sp{str}; + static_assert(std::is_same>::value); + } +#ifdef HAS_STRING_VIEW + { + std::string_view sv{"foo"}; + gsl::span sp{sv}; + static_assert(std::is_same>::value); + } +#endif +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) +} + +TEST(span_test, front_back) +{ + int arr[5] = {1, 2, 3, 4, 5}; + span s{arr}; + EXPECT_TRUE(s.front() == 1); + EXPECT_TRUE(s.back() == 5); + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. front_back"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + span s2; + EXPECT_DEATH(s2.front(), expected); + EXPECT_DEATH(s2.back(), expected); +} + +#if defined(FORCE_STD_SPAN_TESTS) || defined(__cpp_lib_span) && __cpp_lib_span >= 202002L +TEST(span_test, std_span) +{ + // make sure std::span can be constructed from gsl::span + int arr[5] = {1, 2, 3, 4, 5}; + gsl::span gsl_span{arr}; +#if defined(__cpp_lib_ranges) || (defined(_MSVC_STL_VERSION) && defined(__cpp_lib_concepts)) + EXPECT_TRUE(std::to_address(gsl_span.begin()) == gsl_span.data()); + EXPECT_TRUE(std::to_address(gsl_span.end()) == gsl_span.data() + gsl_span.size()); +#endif // __cpp_lib_ranges + + std::span std_span = gsl_span; + EXPECT_TRUE(std_span.data() == gsl_span.data()); + EXPECT_TRUE(std_span.size() == gsl_span.size()); +} +#endif // defined(FORCE_STD_SPAN_TESTS) || defined(__cpp_lib_span) && __cpp_lib_span >= 202002L + +#if defined(__cpp_lib_span) && defined(__cpp_lib_ranges) +// This test covers the changes in PR #1100 +TEST(span_test, msvc_compile_error_PR1100) +{ + int arr[]{1, 7, 2, 9}; + gsl::span sp{arr, std::size(arr)}; + std::ranges::sort(sp); + for (const auto& e : sp) { (void) e; } +} +#endif // defined(__cpp_lib_span) && defined(__cpp_lib_ranges) + +TEST(span_test, empty_span) +{ + span s{}; + EXPECT_TRUE(s.empty()); + EXPECT_TRUE(s.size() == 0); + EXPECT_TRUE(s.data() == nullptr); + + span cs{}; + EXPECT_TRUE(cs.empty()); + EXPECT_TRUE(cs.size() == 0); + EXPECT_TRUE(cs.data() == nullptr); +} + +TEST(span_test, conversions) +{ + int arr[5] = {1, 2, 3, 4, 5}; + +#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) + span s = arr; + span cs = s; +#else // ^^^ deduction guides /// no deduction guides vvv + span s = arr; + span cs = s; +#endif // defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201703L) + + EXPECT_TRUE(cs.size() == s.size()); + EXPECT_TRUE(cs.data() == s.data()); + + span fs = s; + EXPECT_TRUE(fs.size() == s.size()); + EXPECT_TRUE(fs.data() == s.data()); + + span cfs = s; + EXPECT_TRUE(cfs.size() == s.size()); + EXPECT_TRUE(cfs.data() == s.data()); +} + +TEST(span_test, comparison_operators) +{ + int arr1[3] = {1, 2, 3}; + int arr2[3] = {1, 2, 3}; + int arr3[3] = {4, 5, 6}; + + span s1 = arr1; + span s2 = arr2; + span s3 = arr3; + + EXPECT_TRUE(s1 == s2); + EXPECT_FALSE(s1 != s2); + EXPECT_FALSE(s1 == s3); + EXPECT_TRUE(s1 != s3); + EXPECT_TRUE(s1 < s3); + EXPECT_FALSE(s3 < s1); + EXPECT_TRUE(s1 <= s2); + EXPECT_TRUE(s1 <= s3); + EXPECT_FALSE(s3 <= s1); + EXPECT_TRUE(s3 > s1); + EXPECT_FALSE(s1 > s3); + EXPECT_TRUE(s3 >= s1); + EXPECT_TRUE(s1 >= s2); + EXPECT_FALSE(s1 >= s3); +} + +// ...existing code... + +#if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L + +#include // for std::span + +TEST(span_test, compare_empty_span) +{ + gsl::span gsl_s{}; + std::span std_s{}; + + EXPECT_TRUE(gsl_s.empty()); + EXPECT_TRUE(std_s.empty()); + EXPECT_EQ(gsl_s.size(), std_s.size()); + EXPECT_EQ(gsl_s.data(), std_s.data()); +} + +TEST(span_test, compare_subspan) +{ + int arr[5] = {1, 2, 3, 4, 5}; + gsl::span gsl_s = arr; + std::span std_s = arr; + + auto gsl_sub1 = gsl_s.subspan(1); + auto std_sub1 = std_s.subspan(1); + EXPECT_EQ(gsl_sub1.size(), std_sub1.size()); + EXPECT_EQ(gsl_sub1.data(), std_sub1.data()); + + auto gsl_sub2 = gsl_s.subspan(1, 2); + auto std_sub2 = std_s.subspan(1, 2); + EXPECT_EQ(gsl_sub2.size(), std_sub2.size()); + EXPECT_EQ(gsl_sub2.data(), std_sub2.data()); +} + +TEST(span_test, compare_conversions) +{ + int arr[5] = {1, 2, 3, 4, 5}; + gsl::span gsl_s = arr; + std::span std_s = arr; + + gsl::span gsl_cs = gsl_s; + std::span std_cs = std_s; + EXPECT_EQ(gsl_cs.size(), std_cs.size()); + EXPECT_EQ(gsl_cs.data(), std_cs.data()); + + gsl::span gsl_fs = gsl_s; + std::span std_fs = std_s; + EXPECT_EQ(gsl_fs.size(), std_fs.size()); + EXPECT_EQ(gsl_fs.data(), std_fs.data()); + + gsl::span gsl_cfs = gsl_s; + std::span std_cfs = std_s; + EXPECT_EQ(gsl_cfs.size(), std_cfs.size()); + EXPECT_EQ(gsl_cfs.data(), std_cfs.data()); +} + +TEST(span_test, deduction_guides) +{ + int arr[5] = {1, 2, 3, 4, 5}; + std::array std_arr = {1, 2, 3, 4, 5}; + std::vector vec = {1, 2, 3, 4, 5}; + + // Test deduction guides for gsl::span + gsl::span gsl_s1 = arr; + gsl::span gsl_s2 = std_arr; + gsl::span gsl_s3 = vec; + + // Test deduction guides for std::span (for sanity checks) + std::span std_s1 = arr; + std::span std_s2 = std_arr; + std::span std_s3 = vec; + + // Compare sizes + EXPECT_EQ(gsl_s1.size(), std_s1.size()); + EXPECT_EQ(gsl_s2.size(), std_s2.size()); + EXPECT_EQ(gsl_s3.size(), std_s3.size()); + + // Compare data pointers + EXPECT_EQ(gsl_s1.data(), std_s1.data()); + EXPECT_EQ(gsl_s2.data(), std_s2.data()); + EXPECT_EQ(gsl_s3.data(), std_s3.data()); +} + +#endif // defined(__cpp_lib_span) && __cpp_lib_span >= 202002L diff --git a/kernel/third_party/GSL-5.0.0/tests/strict_notnull_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/strict_notnull_tests.cpp new file mode 100644 index 0000000..2879e01 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/strict_notnull_tests.cpp @@ -0,0 +1,450 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include // for not_null, operator<, operator<=, operator> +#include + +#include // for declval + +#include "deathTestCommon.h" + +using namespace gsl; + +#if __cplusplus >= 201703l +using std::void_t; +#else // __cplusplus >= 201703l +template +using void_t = void; +#endif // __cplusplus < 201703l + +// stand-in for a user-defined ref-counted class +template +struct RefCounted +{ + RefCounted(T* p) : p_(p) {} + operator T*() { return p_; } + T* p_; +}; + +namespace +{ +GSL_SUPPRESS(f.4) +bool helper(not_null p) { return *p == 12; } + +GSL_SUPPRESS(f.4) +bool helper_const(not_null p) { return *p == 12; } + +GSL_SUPPRESS(f.4) +bool strict_helper(strict_not_null p) { return *p == 12; } + +GSL_SUPPRESS(f.4) +bool strict_helper_const(strict_not_null p) { return *p == 12; } + +int* return_pointer() { return nullptr; } +} // namespace + +template +static constexpr bool CtorCompilesFor_A = false; +template +static constexpr bool + CtorCompilesFor_A{std::declval()})>> = true; + +template +static constexpr bool CtorCompilesFor_B = false; +template +static constexpr bool CtorCompilesFor_B{N})>> = true; + +template +static constexpr bool DefaultCtorCompilesFor = false; +template +static constexpr bool DefaultCtorCompilesFor{})>> = true; + +template +static constexpr bool CtorCompilesFor_C = false; +template +static constexpr bool CtorCompilesFor_C< + U, void_t{std::declval>()})>> = true; + +TEST(strict_notnull_tests, TestStrictNotNullConstructors) +{ + { + static_assert(CtorCompilesFor_A, "CtorCompilesFor_A"); + static_assert(!CtorCompilesFor_A, "!CtorCompilesFor_A"); + static_assert(!CtorCompilesFor_B, "!CtorCompilesFor_B"); + static_assert(!DefaultCtorCompilesFor, "!DefaultCtorCompilesFor"); + static_assert(!CtorCompilesFor_C, "CtorCompilesFor_C"); +#ifdef CONFIRM_COMPILATION_ERRORS + // Forbid non-nullptr assignable types + strict_not_null> f(std::vector{1}); + strict_not_null z(10); + strict_not_null> y({1, 2}); +#endif + } + + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. TestNotNullConstructors"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + { + // from shared pointer + int i = 12; + auto rp = RefCounted(&i); + strict_not_null p(rp); + EXPECT_TRUE(p.get() == &i); + + strict_not_null> x( + std::make_shared(10)); // shared_ptr is nullptr assignable + + int* pi = nullptr; + EXPECT_DEATH((strict_not_null(pi)), expected); + } + + { + // from unique pointer + strict_not_null> x( + std::make_unique(10)); // unique_ptr is nullptr assignable + + EXPECT_DEATH((strict_not_null>(std::unique_ptr{})), expected); + } + + { + // from pointer to local + int t = 42; + + strict_not_null x{&t}; + helper(&t); + helper_const(&t); + + EXPECT_TRUE(*x == 42); + } + + { + // from raw pointer + // from strict_not_null pointer + + int t = 42; + int* p = &t; + + strict_not_null x{p}; + helper(p); + helper_const(p); + helper(x); + helper_const(x); + + EXPECT_TRUE(*x == 42); + } + + { + // from raw const pointer + // from strict_not_null const pointer + + int t = 42; + const int* cp = &t; + + strict_not_null x{cp}; + helper_const(cp); + helper_const(x); + + EXPECT_TRUE(*x == 42); + } + + { + // from strict_not_null const pointer, using auto + int t = 42; + const int* cp = &t; + + auto x = strict_not_null{cp}; + + EXPECT_TRUE(*x == 42); + } + + { + // from returned pointer + + EXPECT_DEATH(helper(return_pointer()), expected); + EXPECT_DEATH(helper_const(return_pointer()), expected); + } +} + +template +static constexpr bool StrictHelperCompilesFor = false; +template +static constexpr bool + StrictHelperCompilesFor()))>> = true; + +template +static constexpr bool StrictHelperConstCompilesFor = false; +template +static constexpr bool + StrictHelperConstCompilesFor()))>> = + true; + +template +static constexpr bool HelperCompilesFor = false; +template +static constexpr bool HelperCompilesFor()))>> = true; + +TEST(strict_notnull_tests, TestStrictNotNull) +{ + { + // raw ptr <-> strict_not_null + int x = 42; + +#ifdef CONFIRM_COMPILATION_ERRORS + strict_not_null snn = &x; +#endif + static_assert(!StrictHelperCompilesFor, "!StrictHelperCompilesFor"); + static_assert(!StrictHelperConstCompilesFor, "!StrictHelperCompilesFor"); + + const strict_not_null snn1{&x}; + + static_assert(StrictHelperCompilesFor>, + "StrictHelperCompilesFor>"); + helper(snn1); + helper_const(snn1); + + EXPECT_TRUE(*snn1 == 42); + } + + { + // raw ptr <-> strict_not_null + const int x = 42; + +#ifdef CONFIRM_COMPILATION_ERRORS + strict_not_null snn = &x; +#endif + static_assert(!StrictHelperCompilesFor, "!StrictHelperFor"); + static_assert(!StrictHelperConstCompilesFor, + "!StrictHelperCompilesFor"); + + const strict_not_null snn1{&x}; + + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); + static_assert(StrictHelperConstCompilesFor>, + "StrictHelperCompilesFor>"); + helper_const(snn1); + + EXPECT_TRUE(*snn1 == 42); + } + + { + // strict_not_null -> strict_not_null + int x = 42; + + strict_not_null snn1{&x}; + const strict_not_null snn2{&x}; + + strict_helper(snn1); + strict_helper_const(snn1); + strict_helper_const(snn2); + + EXPECT_TRUE(snn1 == snn2); + } + + { + // strict_not_null -> strict_not_null + const int x = 42; + + strict_not_null snn1{&x}; + const strict_not_null snn2{&x}; + + static_assert(!StrictHelperCompilesFor>, + "!StrictHelperCompilesFor>"); + strict_helper_const(snn1); + strict_helper_const(snn2); + + EXPECT_TRUE(snn1 == snn2); + } + + { + // strict_not_null -> not_null + int x = 42; + + strict_not_null snn{&x}; + + const not_null nn1 = snn; + const not_null nn2{snn}; + + helper(snn); + helper_const(snn); + + EXPECT_TRUE(snn == nn1); + EXPECT_TRUE(snn == nn2); + } + + { + // strict_not_null -> not_null + const int x = 42; + + strict_not_null snn{&x}; + + const not_null nn1 = snn; + const not_null nn2{snn}; + + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); + helper_const(snn); + + EXPECT_TRUE(snn == nn1); + EXPECT_TRUE(snn == nn2); + } + + { + // not_null -> strict_not_null + int x = 42; + + not_null nn{&x}; + + const strict_not_null snn1{nn}; + const strict_not_null snn2{nn}; + + strict_helper(nn); + strict_helper_const(nn); + + EXPECT_TRUE(snn1 == nn); + EXPECT_TRUE(snn2 == nn); + + std::hash> hash_snn; + std::hash> hash_nn; + + EXPECT_TRUE(hash_nn(snn1) == hash_nn(nn)); + EXPECT_TRUE(hash_snn(snn1) == hash_nn(nn)); + EXPECT_TRUE(hash_nn(snn1) == hash_nn(snn2)); + EXPECT_TRUE(hash_snn(snn1) == hash_snn(nn)); + } + + { + // not_null -> strict_not_null + const int x = 42; + + not_null nn{&x}; + + const strict_not_null snn1{nn}; + const strict_not_null snn2{nn}; + + static_assert(!StrictHelperCompilesFor>, + "!StrictHelperCompilesFor>"); + strict_helper_const(nn); + + EXPECT_TRUE(snn1 == nn); + EXPECT_TRUE(snn2 == nn); + + std::hash> hash_snn; + std::hash> hash_nn; + + EXPECT_TRUE(hash_nn(snn1) == hash_nn(nn)); + EXPECT_TRUE(hash_snn(snn1) == hash_nn(nn)); + EXPECT_TRUE(hash_nn(snn1) == hash_nn(snn2)); + EXPECT_TRUE(hash_snn(snn1) == hash_snn(nn)); + } +} + +TEST(pointers_test, member_types) +{ + // make sure `element_type` is inherited from `gsl::not_null` + static_assert(std::is_same::element_type, int*>::value, + "check member type: element_type"); +} + +#if defined(__cplusplus) && (__cplusplus >= 201703L) + +TEST(strict_notnull_tests, TestStrictNotNullConstructorTypeDeduction) +{ + const auto terminateHandler = std::set_terminate([] { + std::cerr << "Expected Death. TestStrictNotNullConstructorTypeDeduction"; + std::abort(); + }); + const auto expected = GetExpectedDeathString(terminateHandler); + + { + int i = 42; + + strict_not_null x{&i}; + helper(strict_not_null{&i}); + helper_const(strict_not_null{&i}); + + EXPECT_TRUE(*x == 42); + } + + { + const int i = 42; + + strict_not_null x{&i}; + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); + helper_const(strict_not_null{&i}); + + EXPECT_TRUE(*x == 42); + } + + { + int i = 42; + int* p = &i; + + strict_not_null x{p}; + helper(strict_not_null{p}); + helper_const(strict_not_null{p}); + + EXPECT_TRUE(*x == 42); + } + + { + const int i = 42; + const int* p = &i; + + strict_not_null x{p}; + static_assert(!HelperCompilesFor>, + "!HelperCompilesFor>"); + helper_const(strict_not_null{p}); + + EXPECT_TRUE(*x == 42); + } + + { + auto workaround_macro = []() { + int* p1 = nullptr; + const strict_not_null x{p1}; + }; + EXPECT_DEATH(workaround_macro(), expected); + } + + { + auto workaround_macro = []() { + const int* p1 = nullptr; + const strict_not_null x{p1}; + }; + EXPECT_DEATH(workaround_macro(), expected); + } + + { + int* p = nullptr; + + EXPECT_DEATH(helper(strict_not_null{p}), expected); + EXPECT_DEATH(helper_const(strict_not_null{p}), expected); + } + +#ifdef CONFIRM_COMPILATION_ERRORS + { + strict_not_null x{nullptr}; + helper(strict_not_null{nullptr}); + helper_const(strict_not_null{nullptr}); + } +#endif +} +#endif // #if defined(__cplusplus) && (__cplusplus >= 201703L) diff --git a/kernel/third_party/GSL-5.0.0/tests/utils_tests.cpp b/kernel/third_party/GSL-5.0.0/tests/utils_tests.cpp new file mode 100644 index 0000000..0a79db2 --- /dev/null +++ b/kernel/third_party/GSL-5.0.0/tests/utils_tests.cpp @@ -0,0 +1,236 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#include + +#include // for move +#include // for ldexp +#include +#include // for std::ptrdiff_t +#include // for int32_t, int64_t, uint32_t, uint64_t +#include // for reference_wrapper, _Bind_helper<>::type +#include // for narrow, narrowing_error +#include // finally, narrow_cast +#include // for numeric_limits +#include // for is_same + +using namespace gsl; + +namespace +{ +void f(int& i) { i += 1; } +static int j = 0; +void g() { j += 1; } +} // namespace + +TEST(utils_tests, sanity_check_for_gsl_index_typedef) +{ + static_assert(std::is_same::value, + "gsl::index represents wrong arithmetic type"); +} + +TEST(utils_tests, finally_lambda) +{ + int i = 0; + { + auto _ = finally([&]() { f(i); }); + EXPECT_TRUE(i == 0); + } + EXPECT_TRUE(i == 1); +} + +TEST(utils_tests, finally_lambda_move) +{ + int i = 0; + { + auto _1 = finally([&]() { f(i); }); + { + auto _2 = std::move(_1); + EXPECT_TRUE(i == 0); + } + EXPECT_TRUE(i == 1); + { + auto _2 = std::move(_1); + EXPECT_TRUE(i == 1); + } + EXPECT_TRUE(i == 1); + } + EXPECT_TRUE(i == 1); +} + +TEST(utils_tests, finally_const_lvalue_lambda) +{ + int i = 0; + { + const auto const_lvalue_lambda = [&]() { f(i); }; + auto _ = finally(const_lvalue_lambda); + EXPECT_TRUE(i == 0); + } + EXPECT_TRUE(i == 1); +} + +TEST(utils_tests, finally_mutable_lvalue_lambda) +{ + int i = 0; + { + auto mutable_lvalue_lambda = [&]() { f(i); }; + auto _ = finally(mutable_lvalue_lambda); + EXPECT_TRUE(i == 0); + } + EXPECT_TRUE(i == 1); +} + +TEST(utils_tests, finally_function_with_bind) +{ + int i = 0; + { + auto _ = finally([&i] { return f(i); }); + EXPECT_TRUE(i == 0); + } + EXPECT_TRUE(i == 1); +} + +TEST(utils_tests, finally_function_ptr) +{ + j = 0; + { + auto _ = finally(&g); + EXPECT_TRUE(j == 0); + } + EXPECT_TRUE(j == 1); +} + +TEST(utils_tests, finally_function) +{ + j = 0; + { + auto _ = finally(g); + EXPECT_TRUE(j == 0); + } + EXPECT_TRUE(j == 1); +} + +TEST(utils_tests, narrow_cast) +{ + int n = 120; + char c = narrow_cast(n); + EXPECT_TRUE(c == 120); + + n = 300; + unsigned char uc = narrow_cast(n); + EXPECT_TRUE(uc == 44); +} + +#ifndef GSL_KERNEL_MODE +TEST(utils_tests, static_cast_is_defined) +{ + EXPECT_TRUE(details::static_cast_is_defined(-0.5, std::true_type{})); + EXPECT_FALSE(details::static_cast_is_defined(-1.0, std::true_type{})); + + const double uint32_upper_bound = std::ldexp(1.0, std::numeric_limits::digits); + EXPECT_TRUE(details::static_cast_is_defined(std::nextafter(uint32_upper_bound, 0.0), + std::true_type{})); + EXPECT_FALSE(details::static_cast_is_defined(uint32_upper_bound, std::true_type{})); + + const double int32_lower_bound = -std::ldexp(1.0, std::numeric_limits::digits); + EXPECT_TRUE( + details::static_cast_is_defined(int32_lower_bound - 0.5, std::true_type{})); + EXPECT_FALSE( + details::static_cast_is_defined(int32_lower_bound - 1.0, std::true_type{})); + + const double int32_upper_bound = std::ldexp(1.0, std::numeric_limits::digits); + EXPECT_TRUE(details::static_cast_is_defined(std::nextafter(int32_upper_bound, 0.0), + std::true_type{})); + EXPECT_FALSE(details::static_cast_is_defined(int32_upper_bound, std::true_type{})); + + const float int32_min = static_cast((std::numeric_limits::min)()); + const double int64_min = static_cast((std::numeric_limits::min)()); + EXPECT_TRUE(details::static_cast_is_defined(int32_min, std::true_type{})); + EXPECT_TRUE(details::static_cast_is_defined(int64_min, std::true_type{})); + + EXPECT_TRUE(details::static_cast_is_defined(-1.0, std::true_type{})); + EXPECT_TRUE(details::static_cast_is_defined(0, std::false_type{})); + EXPECT_FALSE(details::static_cast_is_defined(std::numeric_limits::infinity(), + std::true_type{})); + EXPECT_FALSE(details::static_cast_is_defined(-std::numeric_limits::infinity(), + std::true_type{})); + EXPECT_FALSE(details::static_cast_is_defined(std::numeric_limits::quiet_NaN(), + std::true_type{})); +} + +TEST(utils_tests, narrow_exact_signed_minimum) +{ + EXPECT_NO_THROW({ + const auto value = + narrow(static_cast((std::numeric_limits::min)())); + EXPECT_EQ(value, (std::numeric_limits::min)()); + }); + + EXPECT_NO_THROW({ + const auto value = + narrow(static_cast((std::numeric_limits::min)())); + EXPECT_EQ(value, (std::numeric_limits::min)()); + }); +} + +TEST(utils_tests, narrow) +{ + int n = 120; + const char c = narrow(n); + EXPECT_TRUE(c == 120); + + n = 300; + EXPECT_THROW(narrow(n), narrowing_error); + + const auto int32_max = std::numeric_limits::max(); + const auto int32_min = std::numeric_limits::min(); + + EXPECT_TRUE(narrow(int32_t(0)) == 0); + EXPECT_TRUE(narrow(int32_t(1)) == 1); + EXPECT_TRUE(narrow(int32_max) == static_cast(int32_max)); + + EXPECT_THROW(narrow(int32_t(-1)), narrowing_error); + EXPECT_THROW(narrow(int32_min), narrowing_error); + + n = -42; + EXPECT_THROW(narrow(n), narrowing_error); + + EXPECT_TRUE(narrow>(std::complex(4, 2)) == + std::complex(4, 2)); + EXPECT_THROW(narrow>(std::complex(4.2)), narrowing_error); + + EXPECT_TRUE(narrow(float(1)) == 1); + EXPECT_TRUE(narrow(0.0) == false); + EXPECT_TRUE(narrow(1.0) == true); + EXPECT_THROW(narrow(2.0), narrowing_error); + EXPECT_THROW(narrow(256.), narrowing_error); + EXPECT_THROW(narrow(-0.5), narrowing_error); + EXPECT_THROW(narrow(-1.0), narrowing_error); + EXPECT_THROW(narrow((std::numeric_limits::max)()), narrowing_error); + EXPECT_THROW(narrow((std::numeric_limits::lowest)()), narrowing_error); + EXPECT_THROW(narrow(std::numeric_limits::infinity()), narrowing_error); + EXPECT_THROW(narrow(std::numeric_limits::quiet_NaN()), narrowing_error); + + const double int32_lower_bound = -std::ldexp(1.0, std::numeric_limits::digits); + EXPECT_TRUE(narrow(int32_lower_bound) == std::numeric_limits::min()); + EXPECT_THROW(narrow(int32_lower_bound - 0.5), narrowing_error); + + const double int64_upper_bound = std::ldexp(1.0, std::numeric_limits::digits); + const double uint64_upper_bound = std::ldexp(1.0, std::numeric_limits::digits); + EXPECT_THROW(narrow(int64_upper_bound), narrowing_error); + EXPECT_THROW(narrow(uint64_upper_bound), narrowing_error); +} +#endif // GSL_KERNEL_MODE diff --git a/mcp/core/Control_Requests.hpp b/mcp/core/Control_Requests.hpp new file mode 100644 index 0000000..b9f4ac9 --- /dev/null +++ b/mcp/core/Control_Requests.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include "runtime/Plot.hpp" +#include +#include +#include +#include + +namespace aethera::mcp { + +struct Empty_Request {}; + +struct Plot_Request { + std::string plot; /* Gallery Plot 稳定标识。 */ +}; + +struct Component_Request { + std::string plot; /* Gallery Plot 稳定标识。 */ + std::string component; /* Renderable 业务组件标识。 */ +}; + +struct Write_Property_Request { + std::string plot; /* Gallery Plot 稳定标识。 */ + std::string component; /* Renderable 业务组件标识。 */ + std::string property; /* 待修改属性键。 */ + nlohmann::json value; /* 属性协议值。 */ +}; + +struct Generate_Data_Request { + std::string plot; /* Gallery Plot 稳定标识。 */ + nlohmann::json input; /* Plot 数据生成器输入。 */ +}; + +struct Plot_Input_Request { + std::string plot; /* 接收输入的 Gallery Plot。 */ + web::Plot_Input_Event event; /* 带生产者时间的完整输入事件。 */ +}; + +struct Plot_Render_Request { + std::string plot; /* 接收帧请求的 Gallery Plot。 */ + double time_milliseconds{}; /* MCP 调用方单调时间线上的帧时刻。 */ + std::uint32_t width{720}; /* 无媒体订阅时的诊断帧宽度。 */ + std::uint32_t height{420}; /* 无媒体订阅时的诊断帧高度。 */ +}; + +struct Frame_Trace_Request { + std::string plot; /* 捕获逐帧执行数据的 Gallery Plot。 */ + std::size_t frame_count{1}; /* 直接复用 Plot 现有物理帧捕获槽。 */ +}; + +} diff --git a/mcp/core/Control_Service.cpp b/mcp/core/Control_Service.cpp index e39d96e..e90a6f2 100644 --- a/mcp/core/Control_Service.cpp +++ b/mcp/core/Control_Service.cpp @@ -1,34 +1,17 @@ #include "Control_Service.hpp" #include "Control_Service.ipp" +#include "Control_Requests.hpp" #include "Protocol_Type.hpp" #include "runtime/Gallery_Plots.hpp" #include #include +#include #include #include namespace aethera::mcp { namespace { -struct Empty_Request {}; -struct Plot_Request { - std::string plot; /* Gallery Plot 稳定标识。 */ -}; -struct Component_Request { - std::string plot; /* Gallery Plot 稳定标识。 */ - std::string component; /* Renderable 业务组件标识。 */ -}; -struct Write_Property_Request { - std::string plot; /* Gallery Plot 稳定标识。 */ - std::string component; /* Renderable 业务组件标识。 */ - std::string property; /* 待修改属性键。 */ - nlohmann::json value; /* 属性协议值。 */ -}; -struct Generate_Data_Request { - std::string plot; /* Gallery Plot 稳定标识。 */ - nlohmann::json input; /* Plot 数据生成器输入。 */ -}; - using Invoke = Tool_Call_Output (*)(Control_Service&, const nlohmann::json&); using Schema = nlohmann::json (*)(); @@ -128,6 +111,77 @@ template }); } +[[nodiscard]] Tool_Call_Output submit_plot_input( + Control_Service& service, const nlohmann::json& arguments) { + return decode_and_call( + arguments, [&service](auto request) { + const auto plot = service.find_plot(request.plot); + if (!plot) + return Tool_Call_Output{ + Tool_Call_Result::unknown_plot, {}, "unknown plot"}; + const auto time_milliseconds = request.event.time_milliseconds; + plot->submit_input(std::move(request.event)); + return Tool_Call_Output{ + Tool_Call_Result::ok, + {{"accepted", true}, + {"plot", request.plot}, + {"time_milliseconds", time_milliseconds}}, {}}; + }); +} + +[[nodiscard]] Tool_Call_Output render_plot_at( + Control_Service& service, const nlohmann::json& arguments) { + return decode_and_call( + arguments, [&service](const auto& request) { + const auto plot = service.find_plot(request.plot); + if (!plot) + return Tool_Call_Output{ + Tool_Call_Result::unknown_plot, {}, "unknown plot"}; + plot->schedule_render(web::Plot_Render_Tick{ + .issued_at = std::chrono::steady_clock::now(), + .time_milliseconds = request.time_milliseconds, + .width = request.width, + .height = request.height, + .source = Frame_Request_Source::immediate}); + return Tool_Call_Output{ + Tool_Call_Result::ok, + {{"accepted", true}, + {"plot", request.plot}, + {"time_milliseconds", request.time_milliseconds}, + {"width", request.width}, {"height", request.height}}, {}}; + }); +} + +[[nodiscard]] Tool_Call_Output begin_frame_trace( + Control_Service& service, const nlohmann::json& arguments) { + return decode_and_call( + arguments, [&service](const auto& request) { + const auto plot = service.find_plot(request.plot); + if (!plot) + return Tool_Call_Output{ + Tool_Call_Result::unknown_plot, {}, "unknown plot"}; + plot->request_taskflow_trace(request.frame_count); + return Tool_Call_Output{ + Tool_Call_Result::ok, + {{"accepted", true}, {"plot", request.plot}, + {"frame_count", request.frame_count}, + {"status_tool", "aethera_frame_trace_read"}}, {}}; + }); +} + +[[nodiscard]] Tool_Call_Output read_frame_trace( + Control_Service& service, const nlohmann::json& arguments) { + return decode_and_call( + arguments, [&service](const auto& request) { + const auto plot = service.find_plot(request.plot); + if (!plot) + return Tool_Call_Output{ + Tool_Call_Result::unknown_plot, {}, "unknown plot"}; + return Tool_Call_Output{ + Tool_Call_Result::ok, plot->taskflow_trace(), {}}; + }); +} + [[nodiscard]] Tool_Call_Output begin_benchmark( Control_Service& service, const nlohmann::json& arguments) { return decode_and_call(arguments, [&service](const auto& request) { @@ -221,6 +275,14 @@ constexpr std::array operations{ &request_schema, &write_property}, Operation{"aethera_data_generate", "Generate input data for a gallery plot.", &request_schema, &generate_data}, + Operation{"aethera_plot_input", "Submit one timestamped input event without waiting for rendering.", + &request_schema, &submit_plot_input}, + Operation{"aethera_plot_render_at", "Request one diagnostic frame at a caller-owned timeline time.", + &request_schema, &render_plot_at}, + Operation{"aethera_frame_trace_begin", "Capture existing per-frame Taskflow and Datoviz observations.", + &request_schema, &begin_frame_trace}, + Operation{"aethera_frame_trace_read", "Read the current physical-frame trace capture.", + &request_schema, &read_frame_trace}, Operation{"aethera_benchmark_begin", "Reset diagnostics and asynchronously start rendering a plot.", &request_schema, &begin_benchmark}, Operation{"aethera_benchmark_read", "Read benchmark results from the plot's current diagnostics.", diff --git a/mcp/core/runtime/Plot.cpp b/mcp/core/runtime/Plot.cpp index 1f3b29f..0b0d49a 100644 --- a/mcp/core/runtime/Plot.cpp +++ b/mcp/core/runtime/Plot.cpp @@ -298,6 +298,9 @@ nlohmann::json datoviz_observation_json( {"path", magic_enum::enum_name(value.path)}, {"gpu_timing_requested", value.gpu_timing_requested}, {"readback_requested", value.readback_requested}, + {"controller_input_applied", value.controller_input_applied}, + {"prepare_released_after_submission", + value.prepare_released_after_submission}, {"timings_ms", { {"render_domain_queue_wait", milliseconds(value.render_domain_queue_wait_ns)}, @@ -421,8 +424,24 @@ nlohmann::json taskflow_trace_json( namespace { +[[nodiscard]] Event_Timeline_Time input_timeline_time( + double time_milliseconds) { + constexpr long double nanoseconds_per_millisecond{1'000'000.0L}; + constexpr long double maximum_milliseconds = + static_cast(std::numeric_limits::max()) / + nanoseconds_per_millisecond; + if (!std::isfinite(time_milliseconds) || time_milliseconds < 0.0 || + static_cast(time_milliseconds) > maximum_milliseconds) + throw std::invalid_argument( + "input time_milliseconds must be finite, non-negative and representable"); + return Event_Timeline_Time{static_cast( + static_cast(time_milliseconds) * + nanoseconds_per_millisecond)}; +} + template void dispatch_plot_input(Scene_Object& scene, const Plot_Input_Event& input) { + const auto occurred_at = input_timeline_time(input.time_milliseconds); const auto dispatch = [&](auto event) { scene.template submit_stream(std::move(event)); }; @@ -438,13 +457,14 @@ void dispatch_plot_input(Scene_Object& scene, const Plot_Input_Event& input) { case Event_Type::pointer_press: case Event_Type::pointer_release: { auto event = scene.template make_event>( - input.type); + input.type, occurred_at); apply_pointer(*event); dispatch(std::move(event)); break; } case Event_Type::wheel: { - auto event = scene.template make_event>(); + auto event = scene.template make_event>( + occurred_at); apply_pointer(*event); event->pixel_delta_x = input.pixel_delta_x; event->pixel_delta_y = input.pixel_delta_y; @@ -455,7 +475,8 @@ void dispatch_plot_input(Scene_Object& scene, const Plot_Input_Event& input) { } case Event_Type::key_press: case Event_Type::key_release: { - auto event = scene.template make_event(input.type); + auto event = scene.template make_event( + input.type, occurred_at); event->key = input.key; event->native_key = input.native_key; event->modifiers = input.modifiers; @@ -464,7 +485,7 @@ void dispatch_plot_input(Scene_Object& scene, const Plot_Input_Event& input) { break; } default: - dispatch(scene.template make_event(input.type)); + dispatch(scene.template make_event(input.type, occurred_at)); break; } } @@ -523,10 +544,9 @@ struct Plot::Private { std::unique_ptr frame_policy_3d{}; Frame_Scheduler::Timer frame_timer{}; /* 每 Plot/Scene 只有轻量时间轮节点,不持有线程。 */ static constexpr std::size_t scene_frame_capacity{3}; - std::array frame_slots{}; /* Scene 与外接消费者共享生命周期的稳定三缓冲。 */ + std::array frame_slots{}; /* 帧策略拥有并反复调度的稳定三缓冲;Scene 只借用。 */ std::atomic latest_statistics_frame{}; /* 只定位权威帧槽,不保存统计副本。 */ - std::atomic retired_frames{}; /* 多回调生产、唯一 Taskflow 任务消费。 */ - std::atomic_bool retired_frame_task_scheduled{}; + std::atomic retired_frames{}; /* 完成回调返回、唯一帧策略写者消费的物理帧。 */ Scene scene; /* 析构顺序保证 Scene 先停止,再释放物理帧。 */ moodycamel::ConcurrentQueue tick_requests{}; /* 多生产者提交、唯一短任务消费的帧请求流。 */ std::optional deferred_tick{}; /* 仅 tick consumer 任务访问的 latest 延后请求。 */ @@ -545,7 +565,6 @@ struct Plot::Private { std::atomic_uint64_t post_publish_trace_control{}; /* 高 32 位 requested,低 32 位 captured。 */ std::array>, maximum_taskflow_trace_frames> post_publish_trace_slots{}; - Task_Node completion_tail{}; /* Scene 图内固定停在 plot.frame.publish。 */ Task_Graph post_publish_graph{"plot.post_publish"}; /* publish 后外接 DAG;不再占用 Scene render admission。 */ Task_Node post_publish_tail{}; bool has_post_publish_tail{}; @@ -580,15 +599,6 @@ struct Plot::Private { else slot.frame = std::make_unique(Frame_Identity{}); } - if constexpr (std::same_as) { - auto& completion = std::get>(scene) - ->completion_taskflow(); - completion_tail = completion.add("plot.frame.publish", [this] { - publish_completed_frame(); - }); - completion_tail.describe("owner", "plot") - .describe("stage", "completed pixels publish"); - } } template @@ -612,11 +622,8 @@ struct Plot::Private { } [[nodiscard]] bool consume_frame_policy_events() { - const bool configuration_changed = with_frame_policy( + return with_frame_policy( [](auto& policy) { return policy.consume_events(); }); - if (frame_policy_3d) - static_cast(frame_policy_3d->consume_3d_events()); - return configuration_changed; } [[nodiscard]] nlohmann::json schema() const; @@ -630,12 +637,11 @@ struct Plot::Private { void refresh_schedule(); void clock_tick(const Plot_Render_Tick& tick); void render_frame(Plot_Render_Tick tick); - void publish_completed_frame(Render_Frame* completed = nullptr); + void publish_completed_frame(Render_Frame* completed); void consume_completed_frame(Render_Frame* frame); void retire_completed_frame(Render_Frame* frame); void consume_retired_frames(); void finalize_retired_frame(Render_Frame* frame); - void arm_retired_frame_consumer(); void attach_completion(std::unique_ptr completion); [[nodiscard]] bool mark_taskflow_trace(Render_Frame& frame); [[nodiscard]] bool mark_post_publish_taskflow_trace(); @@ -826,6 +832,7 @@ void Plot::Private::release_render_admission(std::weak_ptr lifetime) { } void Plot::Private::consume_tick(std::weak_ptr lifetime) { + consume_retired_frames(); if (consume_frame_policy_events()) refresh_schedule(); const auto observed_generation = tick_request_generation.load(std::memory_order_acquire); @@ -837,10 +844,12 @@ void Plot::Private::consume_tick(std::weak_ptr lifetime) { auto tick = std::exchange(deferred_tick, {}); if (tick) clock_tick(*tick); } + consume_retired_frames(); if (consume_frame_policy_events()) refresh_schedule(); tick_task_scheduled.store(false, std::memory_order_release); if (tick_request_generation.load(std::memory_order_acquire) != observed_generation || + retired_frames.load(std::memory_order_acquire) || (render_admission.load(std::memory_order_acquire) == Render_Admission_State::ready && deferred_tick)) arm_tick_consumer(std::move(lifetime)); @@ -940,7 +949,12 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) { if (terminal_failure.load(std::memory_order_acquire)) return; const auto streams = stream_snapshot(); const auto& pacing = pacing_state(); - if (!pacing.render_enabled || streams.consumers->empty()) return; + const bool direct_diagnostics_frame = + streams.consumers->empty() && + tick.source == Frame_Request_Source::immediate; + if (!pacing.render_enabled || + (streams.consumers->empty() && !direct_diagnostics_frame)) + return; auto admission_expected = Render_Admission_State::ready; if (!render_admission.compare_exchange_strong( @@ -1024,8 +1038,14 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) { }; try { - tick.width = streams.width; - tick.height = streams.height; + if (streams.consumers->empty()) { + tick.width = std::clamp(tick.width, 160U, 1920U) & ~1U; + tick.height = std::clamp(tick.height, 120U, 1080U) & ~1U; + } + else { + tick.width = streams.width; + tick.height = streams.height; + } const std::uint64_t sequence = next_frame_sequence++; const Frame_Identity identity{ sequence, tick.sequence == 0 ? sequence : tick.sequence}; @@ -1128,23 +1148,23 @@ void Plot::Private::render_frame(Plot_Render_Tick tick) { void Plot::Private::publish_completed_frame(Render_Frame* completed) { - Render_Frame* frame{}; + if (!completed) + throw std::invalid_argument("Plot received a null completed frame"); Managed_Frame* managed{}; - for (std::size_t index = 0; index < frame_slots.size(); ++index) { - if (frame_slots[index].state.load(std::memory_order_acquire) != - Frame_State::rendering) - continue; - if (managed) - throw std::logic_error("Plot has multiple frames in Scene rendering"); - frame = std::visit( + for (auto& slot : frame_slots) { + auto* frame = std::visit( [](const auto& value) -> Render_Frame* { return value.get(); }, - frame_slots[index].frame); - if (completed && frame != completed) continue; - managed = &frame_slots[index]; - if (completed) break; + slot.frame); + if (frame != completed) continue; + managed = &slot; + break; } if (!managed) - throw std::logic_error("Scene completion graph has no rendering Plot frame"); + throw std::logic_error("completed frame has no owning Plot policy slot"); + if (managed->state.load(std::memory_order_acquire) != + Frame_State::rendering) + throw std::logic_error("completed Plot frame is not rendering"); + auto* frame = completed; const auto& pacing = pacing_state(); const auto identity = frame->identity(); @@ -1276,18 +1296,6 @@ void Plot::Private::consume_completed_frame(Render_Frame* frame) { } } -void Plot::Private::arm_retired_frame_consumer() { - bool expected{}; - if (!retired_frame_task_scheduled.compare_exchange_strong( - expected, true, std::memory_order_acq_rel, - std::memory_order_acquire)) return; - const auto weak = lifetime; - schedule_task("web.plot.frame.retire", [weak] { - if (const auto owner = weak.lock()) - owner->d->consume_retired_frames(); - }); -} - void Plot::Private::consume_retired_frames() { for (;;) { auto* list = retired_frames.exchange(nullptr, std::memory_order_acq_rel); @@ -1311,9 +1319,6 @@ void Plot::Private::consume_retired_frames() { finalize_retired_frame(frame); } } - retired_frame_task_scheduled.store(false, std::memory_order_release); - if (retired_frames.load(std::memory_order_acquire)) - arm_retired_frame_consumer(); } void Plot::Private::retire_completed_frame(Render_Frame* frame) { @@ -1337,7 +1342,7 @@ void Plot::Private::retire_completed_frame(Render_Frame* frame) { } while (!retired_frames.compare_exchange_weak( head, managed, std::memory_order_release, std::memory_order_relaxed)); - arm_retired_frame_consumer(); + arm_tick_consumer(lifetime); } void Plot::Private::finalize_retired_frame(Render_Frame* frame) { @@ -1354,13 +1359,24 @@ void Plot::Private::finalize_retired_frame(Render_Frame* frame) { if (!managed) throw std::logic_error("retired frame has no owned Plot slot"); - with_frame_policy([&](auto& policy) { - policy.record_frame_completed( - frame->identity().sequence, - managed->submitted_at.time_since_epoch().count() == 0 - ? std::chrono::steady_clock::duration::zero() - : std::chrono::steady_clock::now() - managed->submitted_at); - }); + const auto completion_latency = + managed->submitted_at.time_since_epoch().count() == 0 + ? std::chrono::steady_clock::duration::zero() + : std::chrono::steady_clock::now() - managed->submitted_at; + std::optional datoviz_observation; + if (auto* frame_3d = dynamic_cast(frame)) { + datoviz_observation = frame_3d->take_datoviz_observation(); + if (!datoviz_observation) + throw std::logic_error( + "completed 3D frame has no Datoviz observation"); + frame_policy_3d->record_frame_completed( + frame->identity().sequence, completion_latency, + datoviz_observation->prepare_released_after_submission); + } + else { + frame_policy_2d->record_frame_completed( + frame->identity().sequence, completion_latency); + } const auto statistics_generation_value = statistics_generation.load(std::memory_order_acquire); @@ -1393,10 +1409,8 @@ void Plot::Private::finalize_retired_frame(Render_Frame* frame) { } } captured_components = view->capture_components(executed_components); - if (auto* frame_3d = dynamic_cast(frame)) { - if (auto observation = frame_3d->take_datoviz_observation()) - captured_backend = datoviz_observation_json(*observation); - } + if (datoviz_observation) + captured_backend = datoviz_observation_json(*datoviz_observation); } auto expected = Frame_State::consuming; @@ -1469,32 +1483,24 @@ void Plot::ensure_started() { .source = Frame_Request_Source::periodic}); } }); - /* - * 2D 的 callback 在 Scene render admission 已释放后运行:先执行所有 - * post-publish 外接 DAG;Scene 完成 frame_ready 与 trace 收口后,再由 - * retired callback 归还物理槽。这样 H264(N) 可与 Render(N+1) 重叠。 - */ + /* Scene 回调只表示借用结束;Frame 的发布、统计和复用始终由本帧策略决定。 */ if (auto* scene = std::get_if>(&d->scene)) { (*scene)->set_frame_callback([weak](Frame_2D* frame) { if (auto owner = weak.lock()) { - try { owner->d->consume_completed_frame(frame); } - catch (...) { owner->d->fail(std::current_exception()); } - } - }); - (*scene)->set_frame_retired_callback([weak](Frame_2D* frame) { - if (auto owner = weak.lock()) { - try { owner->d->retire_completed_frame(frame); } + try { + owner->d->publish_completed_frame(frame); + owner->d->consume_completed_frame(frame); + owner->d->retire_completed_frame(frame); + } catch (...) { owner->d->fail(std::current_exception()); } } }); } else { auto& scene_3d = std::get>(d->scene); scene_3d->set_submitted_frame_callback( - [weak](Frame_3D* frame, bool overlaps_gpu) { + [weak](Frame_3D*, bool) { if (auto owner = weak.lock()) { try { - owner->d->frame_policy_3d->record_gpu_submitted( - frame->identity().sequence, overlaps_gpu); owner->d->release_render_admission(weak); } catch (...) { owner->d->fail(std::current_exception()); } @@ -1505,8 +1511,6 @@ void Plot::ensure_started() { if (auto owner = weak.lock()) { try { owner->d->publish_completed_frame(frame); - owner->d->frame_policy_3d->record_gpu_completed( - frame->identity().sequence); owner->d->consume_completed_frame(frame); owner->d->retire_completed_frame(frame); } @@ -1587,6 +1591,12 @@ void Plot::configure_stream(Stream_Id stream, std::uint32_t width, } void Plot::schedule_render(Plot_Render_Tick tick) { + if (!std::isfinite(tick.time_milliseconds) || + tick.time_milliseconds < 0.0) + throw std::invalid_argument( + "render time_milliseconds must be finite and non-negative"); + if (tick.width == 0 || tick.height == 0) + throw std::invalid_argument("render viewport must be non-zero"); ensure_started(); if (d->terminal_failure.load(std::memory_order_acquire)) return; d->submit_tick_request(std::move(tick)); @@ -1605,6 +1615,7 @@ void Plot::render_once() { } void Plot::submit_input(Plot_Input_Event event) { + static_cast(input_timeline_time(event.time_milliseconds)); ensure_started(); if (d->terminal_failure.load(std::memory_order_acquire)) return; /* @@ -1658,6 +1669,8 @@ nlohmann::json Plot::diagnostics() const { Frame_Identity identity{}; std::uint64_t created_time_unix_ns{}; std::uint64_t dropped_sequences{}; + std::uint32_t completed_width{}; + std::uint32_t completed_height{}; double frame_rate{}; bool is_3d{}; const auto read_scene_statistics = [&](const auto& state) { @@ -1688,8 +1701,27 @@ nlohmann::json Plot::diagnostics() const { completed_frame && completed_frame->state.load(std::memory_order_acquire) == Private::Frame_State::available && - completed_frame->statistics_generation == generation) + completed_frame->statistics_generation == generation) { statistics = completed_frame->statistics; + std::visit([&](const auto& frame) { + using Frame_Pointer = + std::remove_cvref_t; + if constexpr (std::same_as< + Frame_Pointer, + std::unique_ptr>) { + const auto image = frame->image(); + completed_width = static_cast( + std::max(0, image.width)); + completed_height = static_cast( + std::max(0, image.height)); + } + else { + const auto extent = frame->extent(); + completed_width = extent.width; + completed_height = extent.height; + } + }, completed_frame->frame); + } completed_frame->diagnostic_readers.fetch_sub( 1, std::memory_order_release); } @@ -1729,8 +1761,12 @@ nlohmann::json Plot::diagnostics() const { const auto native_format = is_3d ? pixel_format_name(Frame_3D::native_pixel_format) : pixel_format_name(Frame_2D::native_pixel_format); + const auto pixel_width = completed_width == 0 + ? stream.width : completed_width; + const auto pixel_height = completed_height == 0 + ? stream.height : completed_height; const std::size_t byte_length = pacing.video_enabled - ? static_cast(stream.width) * stream.height * 4U : 0U; + ? static_cast(pixel_width) * pixel_height * 4U : 0U; nlohmann::json output{ {"protocol", "aethera.plot.diagnostics"}, {"version", 4}, {"dimension", is_3d ? "3D" : "2D"}, @@ -1744,7 +1780,7 @@ nlohmann::json Plot::diagnostics() const { {"frame_rate_fps", frame_rate}, {"dropped_sequence_count", dropped_sequences}, {"window_capacity", diagnostic_window_capacity}, - {"pixel", {{"width", stream.width}, {"height", stream.height}, + {"pixel", {{"width", pixel_width}, {"height", pixel_height}, {"format", format}, {"native_format", native_format}, {"supported_formats", std::move(supported_formats)}, {"byte_length", byte_length}}}, @@ -1756,15 +1792,11 @@ nlohmann::json Plot::diagnostics() const { const auto& pipeline = d->frame_policy_3d->read_state< Frame_Policy_3D::Base_Tag>(); output["frame_policy"]["three_dimensional_pipeline"] = { - {"capacity", pipeline.pipeline_capacity}, - {"gpu_submission_count", pipeline.gpu_submission_count}, + {"capacity", Frame_Policy_3D::pipeline_capacity}, {"overlapped_release_count", pipeline.overlapped_release_count}, {"completion_gated_release_count", pipeline.completion_gated_release_count}, {"gpu_completion_count", pipeline.gpu_completion_count}, - {"gpu_in_flight", pipeline.gpu_in_flight}, - {"peak_gpu_in_flight", pipeline.peak_gpu_in_flight}, - {"last_submitted_sequence", pipeline.last_submitted_sequence}, {"last_completed_sequence", pipeline.last_completed_sequence} }; const auto gpu = gpu_completion_state(); @@ -1859,7 +1891,6 @@ void Plot::reset_diagnostics() { std::visit([](auto& scene) { scene->reset_diagnostics(); }, d->scene); d->statistics_generation.fetch_add(1, std::memory_order_acq_rel); d->with_frame_policy([](auto& policy) { policy.reset_statistics(); }); - if (d->frame_policy_3d) d->frame_policy_3d->reset_3d_statistics(); d->arm_tick_consumer(weak_from_this()); } } diff --git a/mcp/core/runtime/Plot.hpp b/mcp/core/runtime/Plot.hpp index fdc2bb8..48e3fa0 100644 --- a/mcp/core/runtime/Plot.hpp +++ b/mcp/core/runtime/Plot.hpp @@ -14,6 +14,7 @@ namespace aethera::web { struct Gallery_Video_Stream; struct Plot_Input_Event { Event_Type type{Event_Type::pointer_move}; /* 输入事件业务类型。 */ + double time_milliseconds{}; /* 输入生产者单调时间线上的事件发生时刻。 */ render_2d::Point_F position{}; /* Plot 像素坐标。 */ render_2d::Point_F global_position{}; /* 浏览器屏幕像素坐标。 */ Mouse_Button button{Mouse_Button::none}; /* 本次变化涉及的鼠标按键。 */ diff --git a/mcp/main.cmake b/mcp/main.cmake index 72fd301..17f41ba 100644 --- a/mcp/main.cmake +++ b/mcp/main.cmake @@ -70,6 +70,7 @@ if (MSVC) VERBATIM) endif () if (Aethera_BUILD_TESTS) + find_package(benchmark CONFIG REQUIRED) add_executable(Aethera_MCP_Protocol_Type_Tests "${CMAKE_CURRENT_LIST_DIR}/tests/Protocol_Type_Tests.cpp") target_link_libraries(Aethera_MCP_Protocol_Type_Tests PRIVATE @@ -83,6 +84,32 @@ if (Aethera_BUILD_TESTS) COMMAND Aethera_MCP_Protocol_Type_Tests) set_tests_properties(Aethera_MCP_Protocol_Type_Tests PROPERTIES LABELS "Aethera_MCP") + add_executable(Aethera_MCP_Benchmarks + "${CMAKE_CURRENT_LIST_DIR}/tests/Control_Path_Benchmarks.cpp") + target_link_libraries(Aethera_MCP_Benchmarks PRIVATE + Aethera_MCP_Core + benchmark::benchmark + TBB::tbbmalloc_proxy) + renderive_stage_render_3D_runtime(Aethera_MCP_Benchmarks) + if (MSVC) + target_compile_options(Aethera_MCP_Benchmarks PRIVATE + /utf-8 /bigobj) + target_link_options(Aethera_MCP_Benchmarks PRIVATE + "/INCLUDE:__TBB_malloc_proxy") + add_custom_command(TARGET Aethera_MCP_Benchmarks POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "$" + "$" + "$" + VERBATIM) + endif () + add_custom_target(Aethera_MCP_benchmark + COMMAND Aethera_MCP_Benchmarks + --benchmark_min_time=1s + --benchmark_repetitions=3 + --benchmark_report_aggregates_only=true + DEPENDS Aethera_MCP_Benchmarks + USES_TERMINAL) add_custom_target(Aethera_MCP_check COMMAND "${CMAKE_CTEST_COMMAND}" --test-dir "${CMAKE_BINARY_DIR}" diff --git a/mcp/tests/Control_Path_Benchmarks.cpp b/mcp/tests/Control_Path_Benchmarks.cpp new file mode 100644 index 0000000..55678a1 --- /dev/null +++ b/mcp/tests/Control_Path_Benchmarks.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace aethera::mcp::benchmarks { +namespace { + +[[nodiscard]] Plot_Input_Request timestamped_drag_request( + Event_Type type = Event_Type::pointer_move) { + Plot_Input_Request request; + request.plot = "datoviz_point"; + request.event.type = type; + request.event.time_milliseconds = 1'000.0; + request.event.position = {240.0, 180.0}; + request.event.global_position = {240.0, 180.0}; + request.event.button = Mouse_Button::left; + request.event.buttons = 1; + return request; +} + +void decode_timestamped_drag(benchmark::State& state) { + const auto encoded = encode_protocol_value(timestamped_drag_request()); + for ([[maybe_unused]] auto iteration : state) { + Plot_Input_Request decoded; + decode_protocol_value(decoded, encoded); + benchmark::DoNotOptimize(decoded.event.time_milliseconds); + benchmark::ClobberMemory(); + } + state.SetItemsProcessed(state.iterations()); +} + +void decode_timed_render(benchmark::State& state) { + const auto encoded = encode_protocol_value( + Plot_Render_Request{"datoviz_point", 1'000.0, 720, 420}); + for ([[maybe_unused]] auto iteration : state) { + Plot_Render_Request decoded; + decode_protocol_value(decoded, encoded); + benchmark::DoNotOptimize(decoded.time_milliseconds); + benchmark::ClobberMemory(); + } + state.SetItemsProcessed(state.iterations()); +} + +class Control_Path : public benchmark::Fixture { +public: + void SetUp(const benchmark::State&) override { + service = Control_Service::create(); + } + + void TearDown(const benchmark::State&) override { + service.reset(); + } + +protected: + std::shared_ptr service; +}; + +BENCHMARK_DEFINE_F(Control_Path, Timestamped_Drag_Admission)( + benchmark::State& state) { + auto request = timestamped_drag_request(Event_Type::pointer_press); + auto arguments = encode_protocol_value(request); + const auto press = service->call_tool("aethera_plot_input", arguments); + if (press.result != Tool_Call_Result::ok) { + state.SkipWithError("timestamped pointer press was rejected"); + return; + } + + arguments["event"]["type"] = "pointer_move"; + double time_milliseconds = request.event.time_milliseconds; + for ([[maybe_unused]] auto iteration : state) { + time_milliseconds += 1'000.0 / 120.0; + arguments["event"]["time_milliseconds"] = time_milliseconds; + const auto result = service->call_tool( + "aethera_plot_input", arguments); + if (result.result != Tool_Call_Result::ok) { + state.SkipWithError("timestamped pointer move was rejected"); + break; + } + benchmark::DoNotOptimize(result.result); + } + state.SetItemsProcessed(state.iterations()); +} + +BENCHMARK(decode_timestamped_drag); +BENCHMARK(decode_timed_render); +BENCHMARK_REGISTER_F(Control_Path, Timestamped_Drag_Admission) + ->Iterations(512); + +} +} + +int main(int argc, char** argv) { + aethera::initialize_runtime({}); + benchmark::Initialize(&argc, argv); + if (benchmark::ReportUnrecognizedArguments(argc, argv)) return 2; + benchmark::RunSpecifiedBenchmarks(); + benchmark::Shutdown(); + return 0; +} diff --git a/mcp/tests/Protocol_Type_Tests.cpp b/mcp/tests/Protocol_Type_Tests.cpp index a209fc2..aa6fc7c 100644 --- a/mcp/tests/Protocol_Type_Tests.cpp +++ b/mcp/tests/Protocol_Type_Tests.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -69,4 +70,24 @@ TEST(Protocol_Type, Non_Aggregate_Render_Type_Has_Explicit_Matcher) { EXPECT_EQ(decoded, color_map); } +TEST(Protocol_Type, Timestamped_Input_Request_Is_Described_Recursively) { + const auto schema = describe_protocol_type(); + const auto& event = schema.at("properties").at("event"); + EXPECT_TRUE(event.at("properties").contains("time_milliseconds")); + EXPECT_TRUE(event.at("properties").contains("position")); + EXPECT_TRUE(event.at("properties").contains("type")); +} + +TEST(Protocol_Type, Render_Time_Request_Uses_The_Caller_Timeline) { + const Plot_Render_Request request{ + "datoviz_point", 125.5, 720, 420}; + const auto encoded = encode_protocol_value(request); + Plot_Render_Request decoded{}; + decode_protocol_value(decoded, encoded); + EXPECT_EQ(decoded.plot, request.plot); + EXPECT_DOUBLE_EQ(decoded.time_milliseconds, request.time_milliseconds); + EXPECT_EQ(decoded.width, request.width); + EXPECT_EQ(decoded.height, request.height); +} + } diff --git a/render_2D/render_2D/scene/Render_Scene_2D.cpp b/render_2D/render_2D/scene/Render_Scene_2D.cpp index c48f362..5894360 100644 --- a/render_2D/render_2D/scene/Render_Scene_2D.cpp +++ b/render_2D/render_2D/scene/Render_Scene_2D.cpp @@ -9,9 +9,6 @@ Render_Scene_2D::Render_Result_Type Render_Scene_2D::render(Frame_2D* frame) { void Render_Scene_2D::set_frame_callback(Frame_Callback callback) { double_buffer::detail::Internal_Access::get(this).set_frame_callback(this, std::move(callback)); } -void Render_Scene_2D::set_frame_retired_callback(Frame_Callback callback) { - double_buffer::detail::Internal_Access::get(this).set_frame_retired_callback(this, std::move(callback)); -} void Render_Scene_2D::activate_view() { double_buffer::detail::Internal_Access::get(this).set_view_active(this, true); } diff --git a/render_2D/render_2D/scene/Render_Scene_2D.hpp b/render_2D/render_2D/scene/Render_Scene_2D.hpp index a79207d..2a96196 100644 --- a/render_2D/render_2D/scene/Render_Scene_2D.hpp +++ b/render_2D/render_2D/scene/Render_Scene_2D.hpp @@ -34,17 +34,12 @@ struct Render_Scene_2D : Def; using Render_Result_Type = std::expected; using Render_Frame_Type = Frame_2D; - using Retired_Frame_Callback_Capability = void; [[nodiscard]] Render_Result_Type render(Frame_2D* frame); void set_frame_callback(Frame_Callback callback); - void set_frame_retired_callback(Frame_Callback callback); - /* 向调用方拥有的帧合成一次;Scene::advance 到完成像素发布保持不可重入。 */ /* - * 安装完成帧回调;回调在 Scene 像素/发布阶段结束并释放下一帧准入后执行。 - * 因而回调可以继续执行外接编码/发送 Taskflow,同时下一帧 Scene 已可开始。 - * 调用方拥有的 Frame 必须存活到回调返回。 + * 向调用方拥有的 Frame 合成一次。Scene 只借用传入地址;全部绘制、 + * completion Taskflow 和 Trace 收口并清除借用后,唯一回调通知调用方。 */ - /* callback/frame_ready/trace 完全收尾后通知调用方归还物理 Frame 槽。 */ /* * 返回最终像素完成后、帧回调前执行的直接 Taskflow。 * 只能在 Scene 没有运行时修改该图;禁止在执行期间 emplace/erase/clear。 @@ -54,8 +49,6 @@ struct Render_Scene_2D : Def #include -#include #include #include #include @@ -40,7 +40,7 @@ std::expected, Dependency_Graph_Error> Render_Scene_2D:: */ double_buffer::detail::Internal_Access::advance(scene.get()); /* after_advance writes derived DAG diagnostics into the State write side; - * publish that already-computed result before handing the Scene to Plot. */ + * publish that already-computed result before handing the Scene to its caller. */ double_buffer::detail::Internal_Access::advance(scene.get()); return scene; } @@ -60,10 +60,8 @@ struct Render_Scene_2D::Private : Prev_Private { Renderable_2D_Base* object{}; /* 当前 Paint 图中的事件候选对象;Scene 不拥有。 */ Renderable_2D_Base::Private* private_data{}; /* 候选对象的二维能力层;仅在本次 Prepare 分发期间有效。 */ }; - std::mutex render_mutex{}; /* 只保护完成回调和单帧准入。 */ - bool frame_in_flight{}; /* Scene::advance 到像素发布完成的唯一不可重入准入状态。 */ - Frame_Callback frame_callback{}; /* Plot publish 后的外接消费出口。 */ - Frame_Callback frame_retired_callback{}; /* 全链路 trace 完成后的物理帧归还出口。 */ + std::atomic_bool frame_in_flight{}; /* Scene 当前是否仍借用一个外部 Frame。 */ + std::atomic> frame_callback{}; /* Scene 结束借用后的唯一完成通知。 */ std::unique_ptr frame_taskflow{}; ~Private(); Frame_2D* active_frame{}; /* 异步帧 DAG 借用的外部帧;完成回调前保持存活。 */ @@ -85,7 +83,6 @@ struct Render_Scene_2D::Private : Prev_Private { [[nodiscard]] std::expected render(Object* object, Frame_2D* frame); template void set_frame_callback(Object* object, Frame_Callback callback); - template void set_frame_retired_callback(Object* object, Frame_Callback callback); template void set_view_active(Object* object, bool active); template void reset_diagnostics(Object* object); /* CRTP 覆盖:Builder 挂接最终 Private 后安装二维 Scene 的无虚函数业务分派。 */ @@ -332,25 +329,21 @@ Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) { throw std::invalid_argument( "Render_Scene_2D requires a non-null external frame"); frame->mark(Frame_Trace_Marker::scene_render_entered); - Frame_Callback callback; - Frame_Callback retired_callback; - { - std::lock_guard lock(render_mutex); - if (!frame_callback) - throw std::logic_error("Render_Scene_2D requires a frame callback before render"); - if (frame_in_flight) - return std::unexpected(Render_Result::frame_in_flight); - frame_in_flight = true; - callback = frame_callback; - retired_callback = frame_retired_callback; - } + const auto callback = frame_callback.load(std::memory_order_acquire); + if (!callback || !*callback) + throw std::logic_error( + "Render_Scene_2D requires a frame callback before render"); + bool available{}; + if (!frame_in_flight.compare_exchange_strong( + available, true, std::memory_order_acq_rel, + std::memory_order_acquire)) + return std::unexpected(Render_Result::frame_in_flight); const auto release_admission = [this] { - std::lock_guard lock(render_mutex); - frame_in_flight = false; + frame_in_flight.store(false, std::memory_order_release); }; try { /* - * Plot writes properties and tagged input buffers before render(). + * The caller writes properties and tagged input buffers before render(). * Commit the complete Scene object here, before submitting the frame * topology: this advances every attached Renderable, publishes the * current input buffers, and rebuilds dirty Prepare/Paint graphs while @@ -379,14 +372,11 @@ Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) { frame->mark(Frame_Trace_Marker::scene_render_requested); const bool trace_started = aethera::detail::begin_taskflow_trace(*frame); try { - auto completion = [this, object, frame, callback = std::move(callback), - retired_callback = std::move(retired_callback), + auto completion = [this, object, frame, callback, trace_started]() mutable { /* - * render_2d.frame 的原生 Taskflow 已经完成,所有 active_* 只属于 - * Scene::advance -> pixel/publish 的不可重入区。先发布 Scene 自身 - * 渲染统计并释放准入,再进入调用方 callback;callback 可以继续 - * corun 外接 H264/WebRTC DAG,而下一帧已经允许开始。 + * render_2d.frame 的原生 Taskflow 已经完成。先发布 Scene 自身状态、 + * 结束 Trace 并清除全部 Frame 借用,再通过唯一回调通知帧策略。 */ double_buffer::detail::Internal_Access:: current_dependency_graph(object).for_each_bound( @@ -395,27 +385,13 @@ Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) { }); double_buffer::detail::Internal_Access::publish_state(object); - active_frame = nullptr; - frame_target = nullptr; - { - std::lock_guard lock(render_mutex); - frame_in_flight = false; - } - - try { - frame->mark(Frame_Trace_Marker::callback_started); - callback(frame); - frame->mark(Frame_Trace_Marker::callback_finished); - frame->mark(Frame_Trace_Marker::frame_ready); - } - catch (...) { - if (trace_started) - aethera::detail::finish_taskflow_trace(*frame); - throw; - } + frame->mark(Frame_Trace_Marker::frame_ready); if (trace_started) aethera::detail::finish_taskflow_trace(*frame); - if (retired_callback) retired_callback(frame); + active_frame = nullptr; + frame_target = nullptr; + frame_in_flight.store(false, std::memory_order_release); + (*callback)(frame); }; if (frame->taskflow_trace_requested()) aethera::detail::run_taskflow( @@ -428,8 +404,7 @@ Render_Scene_2D::Private::render(Object* object, Frame_2D* frame) { aethera::detail::finish_taskflow_trace(*frame); active_frame = nullptr; frame_target = nullptr; - std::lock_guard lock(render_mutex); - frame_in_flight = false; + frame_in_flight.store(false, std::memory_order_release); throw; } return {}; @@ -496,13 +471,11 @@ void Render_Scene_2D::Private::reset_diagnostics(Object* object) { } template void Render_Scene_2D::Private::set_frame_callback(Object*, Frame_Callback callback) { - std::lock_guard lock(render_mutex); - frame_callback = std::move(callback); -} -template -void Render_Scene_2D::Private::set_frame_retired_callback(Object*, Frame_Callback callback) { - std::lock_guard lock(render_mutex); - frame_retired_callback = std::move(callback); + frame_callback.store( + callback + ? std::make_shared(std::move(callback)) + : std::shared_ptr{}, + std::memory_order_release); } template void Render_Scene_2D::Private::set_view_active(Object* object, bool active) { diff --git a/render_2D/tests/Async_Render_Contract_Test.cpp b/render_2D/tests/Async_Render_Contract_Test.cpp index a39ae77..99dab29 100644 --- a/render_2D/tests/Async_Render_Contract_Test.cpp +++ b/render_2D/tests/Async_Render_Contract_Test.cpp @@ -139,41 +139,36 @@ int main() { auto* resized_partition_frame = new Frame_2D(Frame_Identity{2, 0}); frame->request_taskflow_trace(); resized_partition_frame->request_taskflow_trace(); - scene->set_frame_retired_callback( - [resized_partition_frame, second_frame_valid, trace_valid]( - Frame_2D* retired) { - const auto trace = retired->take_taskflow_trace(); - bool spectrum_hold{}; - bool afterglow_reduce{}; - bool afterglow_color_barrier{}; - for (const auto& graph : trace.graphs) - for (const auto& node : graph.nodes) { - spectrum_hold = spectrum_hold || - node.name == "prepare.hold.partition"; - afterglow_reduce = afterglow_reduce || - node.name == "prepare.normalize"; - afterglow_color_barrier = afterglow_color_barrier || - node.name == "prepare.color.complete"; - } - if (!spectrum_hold || !afterglow_reduce || - !afterglow_color_barrier) - trace_valid->store(false, std::memory_order_relaxed); - if (retired == resized_partition_frame) - std::_Exit(second_frame_valid->load(std::memory_order_relaxed) && - trace_valid->load(std::memory_order_relaxed) - ? 0 - : 1); - }); + const auto validate_trace = [trace_valid](Frame_2D* completed) { + const auto trace = completed->take_taskflow_trace(); + bool spectrum_hold{}; + bool afterglow_reduce{}; + bool afterglow_color_barrier{}; + for (const auto& graph : trace.graphs) + for (const auto& node : graph.nodes) { + spectrum_hold = spectrum_hold || + node.name == "prepare.hold.partition"; + afterglow_reduce = afterglow_reduce || + node.name == "prepare.normalize"; + afterglow_color_barrier = afterglow_color_barrier || + node.name == "prepare.color.complete"; + } + if (!spectrum_hold || !afterglow_reduce || + !afterglow_color_barrier) + trace_valid->store(false, std::memory_order_relaxed); + }; scene->set_frame_callback( [scene, spectrum, waterfall, afterglow, constellation, frame, resized_partition_frame, spectrum_painted, - topology_valid, second_frame_valid](Frame_2D* completed) { + topology_valid, second_frame_valid, trace_valid, + validate_trace](Frame_2D* completed) { const bool valid = completed == frame && spectrum->read_state().sample_count == 512 && topology_valid->load(std::memory_order_relaxed) && contains_color(completed->image()); if (!valid) std::_Exit(1); + validate_trace(completed); spectrum->set<&Spectrum::Prop::partition_count>(2u); waterfall->set<&Waterfall::Prop::partition_grid>( @@ -188,13 +183,20 @@ int main() { spectrum_painted->store(false, std::memory_order_relaxed); scene->set_frame_callback( [resized_partition_frame, second_frame_valid, - topology_valid](Frame_2D* next) { + topology_valid, trace_valid, + validate_trace](Frame_2D* next) { const bool resized_valid = next == resized_partition_frame && topology_valid->load(std::memory_order_relaxed) && contains_color(next->image()); second_frame_valid->store(resized_valid, std::memory_order_relaxed); + validate_trace(next); + std::_Exit( + second_frame_valid->load(std::memory_order_relaxed) && + trace_valid->load(std::memory_order_relaxed) + ? 0 + : 1); }); if (!scene->render(resized_partition_frame)) std::_Exit(2); }); diff --git a/render_3D/render_3D/base/Datoviz_Frame_Observation.hpp b/render_3D/render_3D/base/Datoviz_Frame_Observation.hpp index 42f335d..7773d5a 100644 --- a/render_3D/render_3D/base/Datoviz_Frame_Observation.hpp +++ b/render_3D/render_3D/base/Datoviz_Frame_Observation.hpp @@ -27,6 +27,8 @@ struct Datoviz_Frame_Observation { Datoviz_Frame_Path path{Datoviz_Frame_Path::recorded}; /* 本帧录制命令或复用既有命令。 */ bool gpu_timing_requested{}; /* 本帧是否读取已录制的 GPU 时间戳。 */ bool readback_requested{}; /* 本帧是否请求最终 RGBA 读回。 */ + bool controller_input_applied{}; /* 本帧是否把已提交输入应用到 Datoviz 控制器。 */ + bool prepare_released_after_submission{}; /* 本帧实际提交后是否立即允许下一帧 Prepare。 */ std::uint64_t render_domain_queue_wait_ns{}; /* 唯一 GPU 提交域排队耗时。 */ std::uint64_t apply_ns{}; /* 应用本帧 Visual 数据耗时。 */ std::uint64_t emit_ns{}; /* 生成 Datoviz 帧计划耗时。 */ diff --git a/render_3D/render_3D/detail/Async_Render_Backend.cpp b/render_3D/render_3D/detail/Async_Render_Backend.cpp index 627884f..3de9f10 100644 --- a/render_3D/render_3D/detail/Async_Render_Backend.cpp +++ b/render_3D/render_3D/detail/Async_Render_Backend.cpp @@ -111,7 +111,7 @@ struct Async_Render_Backend::Implementation std::atomic_bool completion_enqueued{}; /* reservation 与提交失败只允许一个退休事实。 */ }; struct Gpu_Completion { - std::shared_ptr pending{}; /* 完成前保持帧目标和回调集合存活。 */ + std::shared_ptr pending{}; /* 完成前保持后台目标、借用地址和回调集合。 */ std::optional result{}; /* fence 正常交付时的结果。 */ std::exception_ptr failure{}; /* 提交域或完成服务的 Unknown Failure。 */ }; @@ -368,6 +368,8 @@ void Async_Render_Backend::Implementation::submit_prepared( std::shared_ptr pending) { pending->backend_frame.observation.render_domain_queue_wait_ns = pending->domain_queue_wait_ns; + pending->backend_frame.observation.prepare_released_after_submission = + overlaps_gpu; pending->output->mark(Frame_Trace_Marker::backend_submit_queued); backend->submit(pending->backend_frame); pending->submitted.store(true, std::memory_order_release); @@ -484,7 +486,8 @@ void Async_Render_Backend::Implementation::dispatch( wheel->angle_delta_x_value()), wheel_step(wheel->pixel_delta_y_value(), wheel->angle_delta_y_value()), - pointer->keyboard_modifiers(), viewport); + pointer->keyboard_modifiers(), viewport, + event->occurred_at.nanoseconds); } else if (pointer_valid && (event->type == Event_Type::pointer_move || @@ -497,7 +500,8 @@ void Async_Render_Backend::Implementation::dispatch( event->type == Event_Type::pointer_move ? held_button(pointer->pointer_buttons()) : pointer->pointer_button(), - pointer->keyboard_modifiers(), viewport); + pointer->keyboard_modifiers(), viewport, + event->occurred_at.nanoseconds); } else if (key) backend->dispatch_key(*key); if ((wheel && pointer_valid) || pointer_valid || key) event->accept(); diff --git a/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp b/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp index 3da2caf..d39752f 100644 --- a/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp +++ b/render_3D/render_3D/detail/Datoviz_Visual_Backend.cpp @@ -2052,7 +2052,8 @@ std::uint64_t Datoviz_Visual_Backend::Private::apply_visual( void Datoviz_Visual_Backend::Private::dispatch_pointer( ::aethera::Event_Type event, float x, float y, ::aethera::Mouse_Button mouse_button, - ::aethera::Keyboard_Modifier keyboard_modifiers, Extent viewport) { + ::aethera::Keyboard_Modifier keyboard_modifiers, Extent viewport, + std::uint64_t occurred_at_ns) { std::lock_guard api_lock(render_context_->api_mutex()); input_changed_ = true; if (applied_camera_ && applied_camera_->controller == Camera_Controller::turntable) { @@ -2075,11 +2076,12 @@ void Datoviz_Visual_Backend::Private::dispatch_pointer( dvz_pointer_emit_position(input_router_, type, x, y, width, height, button(mouse_button), modifiers(keyboard_modifiers), 1.0F, - dvz_input_timestamp_ns(), nullptr); + occurred_at_ns, nullptr); } void Datoviz_Visual_Backend::Private::dispatch_wheel( float x, float y, float delta_x, float delta_y, - ::aethera::Keyboard_Modifier keyboard_modifiers, Extent viewport) { + ::aethera::Keyboard_Modifier keyboard_modifiers, Extent viewport, + std::uint64_t occurred_at_ns) { std::lock_guard api_lock(render_context_->api_mutex()); input_changed_ = true; if (applied_camera_ && @@ -2089,7 +2091,7 @@ void Datoviz_Visual_Backend::Private::dispatch_wheel( dvz_pointer_emit_wheel( input_router_, x, y, static_cast(viewport.width), static_cast(viewport.height), delta_x, delta_y, - modifiers(keyboard_modifiers), 1.0F, dvz_input_timestamp_ns(), nullptr); + modifiers(keyboard_modifiers), 1.0F, occurred_at_ns, nullptr); } void Datoviz_Visual_Backend::Private::dispatch_key( const ::aethera::Key_Event& event) { @@ -2269,6 +2271,7 @@ Datoviz_Visual_Backend::Private::prepare( observation.path = Datoviz_Frame_Path::recorded; observation.gpu_timing_requested = observe; observation.readback_requested = readback; + observation.controller_input_applied = input_changed_; std::uint64_t phase_started = trace_now_ns(); const bool content_changed = !matches_command_structure(scene, visuals); if (content_changed) ++command_revision_; @@ -2566,16 +2569,18 @@ void Datoviz_Visual_Backend::quarantine(Pending_Frame pending) noexcept { void Datoviz_Visual_Backend::dispatch_pointer( Event_Type type, float x, float y, Mouse_Button button, - Keyboard_Modifier modifiers, Extent viewport) { + Keyboard_Modifier modifiers, Extent viewport, + std::uint64_t occurred_at_ns) { static_cast(*d).dispatch_pointer( - type, x, y, button, modifiers, viewport); + type, x, y, button, modifiers, viewport, occurred_at_ns); } void Datoviz_Visual_Backend::dispatch_wheel( float x, float y, float delta_x, float delta_y, - Keyboard_Modifier modifiers, Extent viewport) { + Keyboard_Modifier modifiers, Extent viewport, + std::uint64_t occurred_at_ns) { static_cast(*d).dispatch_wheel( - x, y, delta_x, delta_y, modifiers, viewport); + x, y, delta_x, delta_y, modifiers, viewport, occurred_at_ns); } void Datoviz_Visual_Backend::dispatch_key(const Key_Event& event) { diff --git a/render_3D/render_3D/detail/Datoviz_Visual_Backend.hpp b/render_3D/render_3D/detail/Datoviz_Visual_Backend.hpp index 4acb5e8..cc89bb2 100644 --- a/render_3D/render_3D/detail/Datoviz_Visual_Backend.hpp +++ b/render_3D/render_3D/detail/Datoviz_Visual_Backend.hpp @@ -85,10 +85,12 @@ struct Datoviz_Visual_Backend final void dispatch_pointer(Event_Type type, float x, float y, Mouse_Button button, Keyboard_Modifier modifiers, - Extent viewport); + Extent viewport, + std::uint64_t occurred_at_ns); void dispatch_wheel(float x, float y, float delta_x, float delta_y, Keyboard_Modifier modifiers, - Extent viewport); + Extent viewport, + std::uint64_t occurred_at_ns); void dispatch_key(const Key_Event& event); }; diff --git a/render_3D/render_3D/detail/Datoviz_Visual_Backend.ipp b/render_3D/render_3D/detail/Datoviz_Visual_Backend.ipp index 5e42213..c31e150 100644 --- a/render_3D/render_3D/detail/Datoviz_Visual_Backend.ipp +++ b/render_3D/render_3D/detail/Datoviz_Visual_Backend.ipp @@ -100,10 +100,12 @@ struct Datoviz_Visual_Backend::Private : Prev_Private { void dispatch_pointer(Event_Type type, float x, float y, Mouse_Button button, Keyboard_Modifier modifiers, - Extent viewport); + Extent viewport, + std::uint64_t occurred_at_ns); void dispatch_wheel(float x, float y, float delta_x, float delta_y, Keyboard_Modifier modifiers, - Extent viewport); + Extent viewport, + std::uint64_t occurred_at_ns); void dispatch_key(const Key_Event& event); void abandon_resources() noexcept; void destroy(); diff --git a/render_3D/render_3D/scene/Render_Scene_3D.hpp b/render_3D/render_3D/scene/Render_Scene_3D.hpp index c0b886d..a6b9269 100644 --- a/render_3D/render_3D/scene/Render_Scene_3D.hpp +++ b/render_3D/render_3D/scene/Render_Scene_3D.hpp @@ -58,9 +58,11 @@ struct Render_Scene_3D : Def { [[nodiscard]] Render_Result render(Frame_3D* frame); void set_frame_callback(Frame_Callback callback); void set_submitted_frame_callback(Submitted_Frame_Callback callback); - /* 无等待提交到调用方拥有的帧;最多三个帧槽,资源能力决定提交或完成释放。 */ - /* GPU 实际提交并释放下一次 Prepare 时调用;bool 表示是否与 GPU 重叠。 */ - /* 安装完成帧回调;completion graph 收口后传回同一外部帧。 */ + /* + * 无等待写入调用方拥有的 Frame;Scene 始终只借用地址。最多三个借用可在 + * CPU Prepare、GPU 和读回阶段重叠,完成全部写入并清除借用后调用唯一完成回调。 + */ + /* GPU 实际提交并释放下一次 Prepare 时调用;仅用于调用方继续驱动帧策略。 */ /* * 返回 GPU 完成并回读像素后、帧回调前执行的直接 Taskflow。 * 只能在 Scene 没有运行时修改该图;禁止在执行期间 emplace/erase/clear。 diff --git a/render_3D/render_3D/scene/Render_Scene_3D.ipp b/render_3D/render_3D/scene/Render_Scene_3D.ipp index 67243c1..0f60bd2 100644 --- a/render_3D/render_3D/scene/Render_Scene_3D.ipp +++ b/render_3D/render_3D/scene/Render_Scene_3D.ipp @@ -47,11 +47,10 @@ struct Render_Scene_3D::Private : Prev_Private { * Prepare,使 CPU(N+1)、GPU(N) 和读回(N-1) 能够重叠。 * 3. Image/Text/Volume 等仍共享 Datoviz Scene 资源的 Visual 不在提交点 * 提前释放,而在 GPU 退休后释放;这是一条明确能力边界,不是 fallback。 - * 4. GPU fence 回调只发布 gpu_completed 事实。completion_busy 选择一个 - * 已完成帧执行 completion_graph;该图结束后才归还槽。因此 Scene 和 - * Renderable State、Plot 发布及用户 callback 始终保持唯一完成写者。 - * 5. 全路径不等待 GPU、不复制组件状态、不创建帧外统计副本。帧身份、 - * 像素、Trace 和完成事实都留在其 Frame_Context/Frame 本身。 + * 4. GPU fence 回调只结束当前借用。completion_busy 选择一个已完成帧执行 + * completion_graph;Scene 清除全部借用引用后,以唯一完成回调通知帧策略。 + * 5. Scene 从不拥有 Frame,也不决定其复用时机。全路径不等待 GPU、不复制 + * 组件状态、不创建帧外统计副本,物理事实始终写入调用方拥有的 Frame。 */ static constexpr std::size_t frame_capacity{3}; std::array frame_contexts{}; @@ -199,12 +198,12 @@ void Render_Scene_3D::Private::complete_frame( Object*, Frame_Context* context) { auto* frame = context->frame; const auto callback = frame_callback.load(std::memory_order_acquire); - frame->mark(Frame_Trace_Marker::callback_started); - if (callback && *callback) (*callback)(frame); - frame->mark(Frame_Trace_Marker::callback_finished); frame->mark(Frame_Trace_Marker::frame_ready); if (context->trace_started) aethera::detail::finish_taskflow_trace(*frame); + + /* Scene 仅借用外部 Frame。完成通知前必须先清除所有借用引用并开放 + * Frame_Context;回调之后是否发布、统计或复用只由调用方帧策略决定。 */ context->frame = nullptr; context->events.clear(); context->failure = {}; @@ -214,6 +213,7 @@ void Render_Scene_3D::Private::complete_frame( context->gpu_submitted.store(false, std::memory_order_relaxed); context->gpu_completed.store(false, std::memory_order_relaxed); context->phase.store(Frame_Phase::available, std::memory_order_release); + if (callback && *callback) (*callback)(frame); } inline Render_Scene_3D::Private::Frame_Context* @@ -392,7 +392,7 @@ void Render_Scene_3D::Private::ensure_frame_taskflow(Object* object) { auto* frame_context = context_for(submitted); if (!frame_context) throw std::logic_error( - "3D backend submitted an unowned Scene frame"); + "3D backend submitted a frame not borrowed by Scene"); frame_context->overlaps_gpu = overlaps_gpu; frame_context->gpu_submitted.store( true, std::memory_order_release); @@ -403,7 +403,7 @@ void Render_Scene_3D::Private::ensure_frame_taskflow(Object* object) { auto* frame_context = context_for(completed); if (!frame_context) throw std::logic_error( - "3D backend completed an unowned Scene frame"); + "3D backend completed a frame not borrowed by Scene"); frame_context->failure = std::move(failure); if (frame_context->failure && !frame_context->gpu_submitted.load( @@ -471,7 +471,7 @@ Render_Scene_3D::Render_Result Render_Scene_3D::Private::render(Object* object, }; try { /* - * Plot 在 render() 前写入 viewport,Visual 也可能在同一帧更新属性。 + * 调用方在 render() 前写入 viewport,Visual 也可能在同一帧更新属性。 * 在创建或执行 frame_taskflow 前一次性推进完整 Scene,确保公共 * Render_Graph_Tag 已构建并且 frame DAG 引用的是同一权威运行图。 */ diff --git a/web_server/src/Graph_WebSocket.cpp b/web_server/src/Graph_WebSocket.cpp index 5d9c4e7..128b1c1 100644 --- a/web_server/src/Graph_WebSocket.cpp +++ b/web_server/src/Graph_WebSocket.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -93,11 +94,17 @@ void Graph_WebSocket::receive(std::string_view message) { const auto type = magic_enum::enum_cast( input->value("type", std::string{})); if (!type) return; + const auto source_time = input->find("time_milliseconds"); + if (source_time == input->end() || !source_time->is_number()) return; + const auto time_milliseconds = source_time->get(); + if (!std::isfinite(time_milliseconds) || time_milliseconds < 0.0) + return; const auto viewport = d->viewport.load(std::memory_order_acquire); const auto width = static_cast(viewport >> 32U); const auto height = static_cast(viewport); Plot_Input_Event decoded; decoded.type = *type; + decoded.time_milliseconds = time_milliseconds; const auto read_point = [&](std::string_view key, render_2d::Point_F& point, bool viewport_relative) { diff --git a/webapp_gallery/src/app.tsx b/webapp_gallery/src/app.tsx index c9c14bd..66986e9 100644 --- a/webapp_gallery/src/app.tsx +++ b/webapp_gallery/src/app.tsx @@ -100,6 +100,7 @@ type Datoviz_Frame_Observation = { path: "recorded" | "reused"; gpu_timing_requested: boolean; readback_requested: boolean; + controller_input_applied: boolean; timings_ms: {render_domain_queue_wait: number; apply: number; emit: number; execute: number; submit: number; gpu_fence_wait: number; readback: number}; traffic: {uploaded_bytes: number; readback_bytes: number}; @@ -707,10 +708,11 @@ function use_plot_stream(plot: Plot, const surface = surface_ref.current; if (detail?.plot_id !== plot.id || !surface) return; surface.focus({preventScroll: true}); + const time_milliseconds = performance.now(); transmit("input", {type: "key_press", key: "home", native_key: 36, - modifiers: 0, auto_repeat: false}); + modifiers: 0, auto_repeat: false, time_milliseconds}); transmit("input", {type: "key_release", key: "home", native_key: 36, - modifiers: 0, auto_repeat: false}); + modifiers: 0, auto_repeat: false, time_milliseconds}); }; const on_diagnostics_reset = (event: Event) => { const detail = (event as CustomEvent<{plot_id: string}>).detail; @@ -781,7 +783,8 @@ function use_plot_stream(plot: Plot, const pointer_payload = (type: string, event: PointerEvent) => ({ type, position: point(event), global_position: global_point(event), button: type === "pointer_move" ? active_button(event) : button(event.button), - buttons: event.buttons, modifiers: modifiers(event) + buttons: event.buttons, modifiers: modifiers(event), + time_milliseconds: event.timeStamp }); const on_pointer_move = (event: PointerEvent) => { event.stopPropagation(); @@ -813,7 +816,7 @@ function use_plot_stream(plot: Plot, }; const on_pointer_leave = (event: PointerEvent) => { event.stopPropagation(); - transmit("input", {type: "leave"}); + transmit("input", {type: "leave", time_milliseconds: event.timeStamp}); }; const on_wheel = (event: WheelEvent) => { event.preventDefault(); @@ -825,7 +828,8 @@ function use_plot_stream(plot: Plot, modifiers: modifiers(event), pixel_delta_x: -event.deltaX * scale, pixel_delta_y: -event.deltaY * scale, angle_delta_x: Math.max(-120, Math.min(120, -event.deltaX * scale)), - angle_delta_y: Math.max(-120, Math.min(120, -event.deltaY * scale))}; + angle_delta_y: Math.max(-120, Math.min(120, -event.deltaY * scale)), + time_milliseconds: event.timeStamp}; const sent = transmit("input", next); record_browser_input("wheel", event.timeStamp, sent); }; @@ -838,13 +842,15 @@ function use_plot_stream(plot: Plot, event.stopPropagation(); const sent = transmit("input", {type, key: key_name(event.key), native_key: event.keyCode, modifiers: modifiers(event), - auto_repeat: event.repeat}); + auto_repeat: event.repeat, time_milliseconds: event.timeStamp}); record_browser_input(type, event.timeStamp, sent); }; const on_key_down = on_key("key_press"); const on_key_up = on_key("key_release"); - const on_focus = () => transmit("input", {type: "show"}); - const on_blur = () => transmit("input", {type: "hide"}); + const on_focus = () => transmit("input", { + type: "show", time_milliseconds: performance.now()}); + const on_blur = () => transmit("input", { + type: "hide", time_milliseconds: performance.now()}); const on_context = (event: MouseEvent) => { event.preventDefault(); event.stopPropagation(); @@ -2183,13 +2189,6 @@ function Taskflow_Timeline({graph, executions, frame, components, gallery_state, `${label} ${milliseconds(end - start)}`, label, `${start_key} → ${end_key}`); }); - add_lifecycle_group("lifecycle-publish", "完成与发布", "Scene 完成 → callback → frame ready"); - const callback_started = marker("callback_started"); - const callback_finished = marker("callback_finished"); - if (callback_started !== undefined && callback_finished !== undefined) - add_item("lifecycle-callback", "lifecycle-publish", "lifecycle", callback_started, callback_finished, - `完成回调 ${milliseconds(callback_finished - callback_started)}`, "完成帧回调", - `其中 Plot publish 测量 ${milliseconds(measurements.plot_publish_ns ?? 0)}`); } groups.push({id: "topology", title:
Topology {graph.stage}
, node_name: graph.name, type: "topology"});