From a5e52540fb9241a9e645aeb7d67457c2cd679076 Mon Sep 17 00:00:00 2001 From: Saket Date: Fri, 8 Mar 2024 23:06:01 +0530 Subject: [PATCH] feat(transient-vector): adds initial support for transient vector --- include/cpp/jank/runtime/erasure.hpp | 6 + .../jank/runtime/obj/persistent_vector.hpp | 19 ++- .../cpp/jank/runtime/obj/transient_vector.hpp | 60 +++++++++ include/cpp/jank/runtime/object.hpp | 1 + .../jank/runtime/obj/persistent_vector.cpp | 6 + src/cpp/jank/runtime/obj/transient_vector.cpp | 116 ++++++++++++++++++ 6 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 include/cpp/jank/runtime/obj/transient_vector.hpp create mode 100644 src/cpp/jank/runtime/obj/transient_vector.cpp diff --git a/include/cpp/jank/runtime/erasure.hpp b/include/cpp/jank/runtime/erasure.hpp index d0c5e4d0a..0cdda692c 100644 --- a/include/cpp/jank/runtime/erasure.hpp +++ b/include/cpp/jank/runtime/erasure.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -179,6 +180,11 @@ namespace jank::runtime return fn(expect_object(erased), std::forward(args)...); } break; + case object_type::transient_vector: + { + return fn(expect_object(erased), std::forward(args)...); + } + break; case object_type::persistent_set: { return fn(expect_object(erased), std::forward(args)...); diff --git a/include/cpp/jank/runtime/obj/persistent_vector.hpp b/include/cpp/jank/runtime/obj/persistent_vector.hpp index c53af002d..7557c0a24 100644 --- a/include/cpp/jank/runtime/obj/persistent_vector.hpp +++ b/include/cpp/jank/runtime/obj/persistent_vector.hpp @@ -5,9 +5,19 @@ namespace jank::runtime { + namespace obj + { + using persistent_vector = static_object; + using persistent_vector_ptr = native_box; + + using transient_vector = static_object; + using transient_vector_ptr = native_box; + } + template <> struct static_object : gc { + using transient_type = static_object; using value_type = runtime::detail::native_persistent_vector; static constexpr bool pointer_free{ false }; @@ -51,15 +61,12 @@ namespace jank::runtime /* behavior::consable */ native_box cons(object_ptr head) const; + /* behavior::transientable */ + obj::transient_vector_ptr to_transient() const; + object base{ object_type::persistent_vector }; value_type data; option meta; mutable native_hash hash{}; }; - - namespace obj - { - using persistent_vector = static_object; - using persistent_vector_ptr = native_box; - } } diff --git a/include/cpp/jank/runtime/obj/transient_vector.hpp b/include/cpp/jank/runtime/obj/transient_vector.hpp new file mode 100644 index 000000000..33e318458 --- /dev/null +++ b/include/cpp/jank/runtime/obj/transient_vector.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include + +namespace jank::runtime +{ + template <> + struct static_object : gc + { + static constexpr bool pointer_free{ false }; + + using value_type = detail::native_transient_vector; + using persistent_type = static_object; + + static_object() = default; + static_object(static_object &&) = default; + static_object(static_object const &) = default; + static_object(detail::native_persistent_vector const &d); + static_object(detail::native_persistent_vector &&d); + static_object(value_type &&d); + + static native_box empty() + { + static auto const ret(make_box()); + return ret; + } + + /* behavior::objectable */ + native_bool equal(object const &) const; + native_persistent_string to_string() const; + void to_string(fmt::memory_buffer &buff) const; + native_hash to_hash() const; + + /* behavior::countable */ + size_t count() const; + + /* behavior::consable_in_place */ + native_box cons_in_place(object_ptr head); + + /* behavior::persistentable */ + native_box to_persistent(); + + /* behavior::callable */ + object_ptr call(object_ptr) const; + object_ptr call(object_ptr, object_ptr) const; + + void assert_active() const; + + object base{ object_type::transient_vector }; + value_type data; + mutable native_hash hash{}; + native_bool active{ true }; + }; + + namespace obj + { + using transient_vector = static_object; + using transient_vector_ptr = native_box; + } +} diff --git a/include/cpp/jank/runtime/object.hpp b/include/cpp/jank/runtime/object.hpp index 85dae4fc8..0a1e19531 100644 --- a/include/cpp/jank/runtime/object.hpp +++ b/include/cpp/jank/runtime/object.hpp @@ -23,6 +23,7 @@ namespace jank::runtime persistent_hash_map, persistent_hash_map_sequence, transient_hash_map, + transient_vector, persistent_set, cons, range, diff --git a/src/cpp/jank/runtime/obj/persistent_vector.cpp b/src/cpp/jank/runtime/obj/persistent_vector.cpp index 5b143e3ad..0418d9dfd 100644 --- a/src/cpp/jank/runtime/obj/persistent_vector.cpp +++ b/src/cpp/jank/runtime/obj/persistent_vector.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace jank::runtime { @@ -102,6 +103,11 @@ namespace jank::runtime return ret; } + obj::transient_vector_ptr obj::persistent_vector::to_transient() const + { + return make_box(data); + } + object_ptr obj::persistent_vector::with_meta(object_ptr const m) const { auto const meta(behavior::detail::validate_meta(m)); diff --git a/src/cpp/jank/runtime/obj/transient_vector.cpp b/src/cpp/jank/runtime/obj/transient_vector.cpp new file mode 100644 index 000000000..f1bb1dc96 --- /dev/null +++ b/src/cpp/jank/runtime/obj/transient_vector.cpp @@ -0,0 +1,116 @@ +#include "jank/runtime/detail/object_util.hpp" +#include "jank/runtime/erasure.hpp" +#include "jank/type.hpp" +#include +#include +#include +#include + +namespace jank::runtime +{ + obj::transient_vector::static_object(runtime::detail::native_persistent_vector &&d) + : data{ d.transient() } + { + } + + obj::transient_vector::static_object(runtime::detail::native_persistent_vector const &d) + : data{ d.transient() } + { + } + + obj::transient_vector::static_object(runtime::detail::native_transient_vector &&d) + : data{ d } + { + } + + native_bool obj::transient_vector::equal(object const &o) const + { + /* Transient equality, in Clojure, is based solely on identity. */ + return &base == &o; + } + + native_persistent_string obj::transient_vector::to_string() const + { + fmt::memory_buffer buff; + to_string(buff); + return native_persistent_string{ buff.data(), buff.size() }; + } + + void obj::transient_vector::to_string(fmt::memory_buffer &buff) const + { + auto inserter(std::back_inserter(buff)); + fmt::format_to(inserter, "{}@{}", magic_enum::enum_name(base.type), fmt::ptr(&base)); + } + + native_hash obj::transient_vector::to_hash() const + { + /* Hash is also based only on identity. Clojure uses default hashCode, which does the same. */ + return static_cast(reinterpret_cast(this)); + } + + size_t obj::transient_vector::count() const + { + assert_active(); + return data.size(); + } + + obj::transient_vector_ptr obj::transient_vector::cons_in_place(object_ptr const head) + { + assert_active(); + data.push_back(head); + return this; + } + + native_box obj::transient_vector::to_persistent() + { + assert_active(); + active = false; + return make_box(std::move(data)); + } + + object_ptr obj::transient_vector::call(object_ptr const o) const + { + assert_active(); + if(o->type == object_type::integer) + { + auto const i(expect_object(o)->data); + if(i < 0 || data.size() <= static_cast(i)) + { + return obj::nil::nil_const(); + } + + return data.at(i); + } + else + { + throw std::runtime_error{ fmt::format("key must be an integer; found {}", runtime::detail::to_string(o))}; + } + } + + object_ptr obj::transient_vector::call(object_ptr const o, object_ptr const fallback) const + { + assert_active(); + if(o->type == object_type::integer) + { + auto const i(expect_object(o)->data); + if(i < 0 || data.size() <= static_cast(i)) + { + return fallback; + } + + return data.at(i); + } + else + { + throw std::runtime_error{ fmt::format("key must be an integer; found {}", runtime::detail::to_string(o))}; + } + } + + void obj::transient_vector::assert_active() const + { + if(!active) + { + throw std::runtime_error{ "transient used after it's been made persistent" }; + } + } +}