diff --git a/src/core_ffi/CMakeLists.txt b/src/core_ffi/CMakeLists.txt index d84dda1..c7db17d 100644 --- a/src/core_ffi/CMakeLists.txt +++ b/src/core_ffi/CMakeLists.txt @@ -15,7 +15,7 @@ if (KLSK_PYTHON_FFI) py_ffi/binder.cc py_ffi/wrapper/short_code.cc py_ffi/wrapper/common_codec.cc - py_ffi/wrapper/py_cases.cc + py_ffi/wrapper/cases.cc py_ffi/wrapper/py_group_union.cc py_ffi/wrapper/py_group.cc ) diff --git a/src/core_ffi/py_ffi/binder.cc b/src/core_ffi/py_ffi/binder.cc index 836753b..b1a37e1 100644 --- a/src/core_ffi/py_ffi/binder.cc +++ b/src/core_ffi/py_ffi/binder.cc @@ -8,15 +8,19 @@ #include "py_cases.h" #include "py_group.h" +#include "py_exception.h" + namespace py = pybind11; using klotski::ffi::PyCases; -using klotski::ffi::PyCasesIter; +//using klotski::ffi::PyCasesIter; -using klotski::ffi::PyCodecExp; +//using klotski::ffi::PyCodecExp; using klotski::ffi::PyShortCode; using klotski::ffi::PyCommonCode; +using klotski::ffi::PyExc_CodecError; + using klotski::ffi::PyGroup; using klotski::ffi::PyGroupUnion; @@ -90,20 +94,24 @@ static PyCases all_cases() { } PYBIND11_MODULE(klotski, m) { - py::register_exception(m, "CodecExp", PyExc_ValueError); +// py::register_exception(m, "CodecExp", PyExc_ValueError); + py::register_exception(m, "CodecError", PyExc_ValueError); m.def("all_cases", &all_cases); m.def("group_demo", &group_demo); - py::class_(m, "PyCases") + auto py_cases = py::class_(m, "Cases") .def("size", &PyCases::size) - .def("__iter__", [](const PyCases &self) { - return PyCasesIter(self); - }, py::keep_alive<0, 1>()); + .def("__iter__", &PyCases::common_codes, py::keep_alive<0, 1>()) + .def("short_codes", &PyCases::short_codes, py::keep_alive<0, 1>()); + + py::class_(py_cases, "ShortCodeIter") + .def("__iter__", [](PyCases::ShortCodeIter &it) -> PyCases::ShortCodeIter& { return it; }) + .def("__next__", &PyCases::ShortCodeIter::next); - py::class_(m, "PyCasesIter") - .def("__iter__", [](PyCasesIter &it) -> PyCasesIter& { return it; }) - .def("__next__", &PyCasesIter::next); + py::class_(py_cases, "CommonCodeIter") + .def("__iter__", [](PyCases::CommonCodeIter &it) -> PyCases::CommonCodeIter& { return it; }) + .def("__next__", &PyCases::CommonCodeIter::next); bind_short_code(m); bind_common_code(m); diff --git a/src/core_ffi/py_ffi/include/py_cases.h b/src/core_ffi/py_ffi/include/py_cases.h index 2a4e7c1..576ff45 100644 --- a/src/core_ffi/py_ffi/include/py_cases.h +++ b/src/core_ffi/py_ffi/include/py_cases.h @@ -2,128 +2,82 @@ #pragma once -#include - #include -#include "ranges/ranges.h" - #include "py_common_code.h" +#include "ranges/ranges.h" namespace klotski::ffi { -using klotski::cases::RangesUnion; - -//class PyCases { -//public: -// PyCases() = delete; -// -// PyCases(PyCases &&cases) noexcept : real_data_(std::move(cases.real_data_)), data_(real_data_) { -// std::cout << "PyCases(PyCases &&) called" << std::endl; -// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; -// } -// -// PyCases(const PyCases &cases) : real_data_(cases.real_data_), data_(real_data_) { -// std::cout << "PyCases(const PyCases &) called" << std::endl; -// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; -// } -// -// PyCases& operator==(PyCases &&cases) { -// std::cout << "PyCases& operator==(PyCases &&) called" << std::endl; -// if (this != &cases) { -// real_data_ = std::move(cases.real_data_); -// } -// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; -// return *this; -// } -// -// PyCases& operator==(const PyCases &&cases) { -// std::cout << "PyCases& operator==(const PyCases &&) called" << std::endl; -// if (this != &cases) { -// real_data_ = cases.real_data_; -// } -// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; -// return *this; -// } -// -// [[nodiscard]] size_t size() const; -// -// static PyCases from(RangesUnion &&data); -// -// static PyCases from_ref(const RangesUnion &data); -// -// [[nodiscard]] const RangesUnion& data_ref() const; -// -//private: -// explicit PyCases(RangesUnion &&data) : real_data_(std::move(data)), data_(real_data_) { -// std::cout << "PyCases init from r-value" << std::endl; -// std::cout << "real_data / data_ref -> " << &real_data_ << " " << &data_ << std::endl; -// } -// -// explicit PyCases(const RangesUnion &data) : data_(data) { -// std::cout << "PyCases init from l-value" << std::endl; -// } -// -// RangesUnion real_data_; -// const RangesUnion &data_; -//}; +using cases::RangesUnion; class PyCases { public: - using Cases = RangesUnion; - using CasesRef = std::reference_wrapper; + PyCases() = delete; - static PyCases from(RangesUnion &&data) { - return PyCases(std::move(data)); - } + // ------------------------------------------------------------------------------------- // - static PyCases from_ref(const RangesUnion &data) { - return PyCases(data); - } + class CommonCodeIter { + public: + PyCommonCode next(); + explicit CommonCodeIter(const RangesUnion &data); - [[nodiscard]] size_t size() const; + private: + uint8_t head_ {0}; + uint32_t index_ {0}; + const RangesUnion &data_; + }; - [[nodiscard]] const Cases& ref() const { + class ShortCodeIter { + public: + PyShortCode next(); + explicit ShortCodeIter(CommonCodeIter iter); - // if (std::holds_alternative(data_)) { - // return std::get(data_); - // } else { - // return std::get(data_); - // } + private: + CommonCodeIter iter_; + }; - return std::visit([](T &&arg) -> const Cases& { - if constexpr (std::is_same_v, Cases>) { - return arg; - } else { - return arg.get(); - } - }, data_); - } + // ------------------------------------------------------------------------------------- // -private: - std::variant data_; + /// Constructing from rvalue. + static PyCases from(RangesUnion &&data) noexcept; - explicit PyCases(const RangesUnion &data) : data_(std::cref(data)) { - std::cout << "PyCases(const RangesUnion &) called" << std::endl; - std::cout << "variant data index: " << data_.index() << std::endl; - } + /// Constructing from longer-lived reference. + static PyCases from_ref(const RangesUnion &data) noexcept; - explicit PyCases(RangesUnion &&data) : data_(std::move(data)) { - std::cout << "PyCases(RangesUnion &&) called" << std::endl; - std::cout << "variant data index: " << data_.index() << std::endl; - } -}; + // ------------------------------------------------------------------------------------- // -class PyCasesIter { -public: - explicit PyCasesIter(const PyCases &data) : data_(data) {} + /// Get the number of cases. + [[nodiscard]] size_t size() const; - PyCommonCode next(); + /// Get ShortCode iterator of cases. + [[nodiscard]] ShortCodeIter short_codes() const; + + /// Get CommonCode iterator of cases. + [[nodiscard]] CommonCodeIter common_codes() const; + + /// Get the CommonCode of the specified index. + [[nodiscard]] PyCommonCode operator[](size_t index) const; + + // ------------------------------------------------------------------------------------- // + + // TODO: add len / repr private: - uint64_t head_ {0}; - size_t index_ {0}; - const PyCases &data_; + explicit PyCases(RangesUnion &&data); + explicit PyCases(const RangesUnion &data); + + // ------------------------------------------------------------------------------------- // + + /// Get const reference of the cases data. + [[nodiscard]] const RangesUnion& data_ref() const noexcept; + + /// Stores actual cases data or its references. + std::variant> data_; + + // ------------------------------------------------------------------------------------- // }; +// TODO: allow compare + } // namespace klotski::ffi diff --git a/src/core_ffi/py_ffi/include/py_exps.h b/src/core_ffi/py_ffi/include/py_exps.h index 8d13379..07efe5f 100644 --- a/src/core_ffi/py_ffi/include/py_exps.h +++ b/src/core_ffi/py_ffi/include/py_exps.h @@ -4,19 +4,19 @@ namespace klotski::ffi { -class PyCodecExp final : std::exception { -public: - explicit PyCodecExp(const std::string_view &msg) : msg_(msg) {} - - ~PyCodecExp() override = default; - - [[nodiscard]] const char* what() const noexcept override { - return msg_.c_str(); - } - -private: - std::string msg_; -}; +//class PyCodecExp final : std::exception { +//public: +// explicit PyCodecExp(const std::string_view &msg) : msg_(msg) {} +// +// ~PyCodecExp() override = default; +// +// [[nodiscard]] const char* what() const noexcept override { +// return msg_.c_str(); +// } +// +//private: +// std::string msg_; +//}; class PyGroupExp final : std::exception { public: diff --git a/src/core_ffi/py_ffi/wrapper/cases.cc b/src/core_ffi/py_ffi/wrapper/cases.cc new file mode 100644 index 0000000..cc3cba1 --- /dev/null +++ b/src/core_ffi/py_ffi/wrapper/cases.cc @@ -0,0 +1,98 @@ +#include + +#include "include/py_cases.h" + +namespace py = pybind11; + +using namespace klotski::ffi; + +using ShortCodeIter = PyCases::ShortCodeIter; +using CommonCodeIter = PyCases::CommonCodeIter; + +// ----------------------------------------------------------------------------------------- // + +CommonCodeIter::CommonCodeIter(const RangesUnion &data) : data_(data) {} + +ShortCodeIter::ShortCodeIter(PyCases::CommonCodeIter iter) : iter_(iter) {} + +PyShortCode ShortCodeIter::next() { + return iter_.next().short_code(); +} + +PyCommonCode CommonCodeIter::next() { + while (head_ < 16) { + const auto &ranges = data_[head_]; + if (index_ < ranges.size()) { + auto code = (static_cast(head_) << 32) | ranges[index_++]; + return std::bit_cast(code); + } + ++head_; + index_ = 0; + } + throw py::stop_iteration(); +} + +// ----------------------------------------------------------------------------------------- // + +size_t PyCases::size() const { + size_t num = 0; + for (const auto &x : data_ref()) { // TODO: fetch from RangesUnion.size() + num += x.size(); + } + return num; +} + +auto PyCases::short_codes() const -> ShortCodeIter { + return ShortCodeIter(common_codes()); +} + +auto PyCases::common_codes() const -> CommonCodeIter { + return CommonCodeIter(data_ref()); +} + +PyCommonCode PyCases::operator[](size_t index) const { + + if (index >= size()) { + throw py::index_error("cases index out of range"); + } + + uint64_t head = 0; + for (;;) { + if (index >= data_ref()[head].size()) { + index -= data_ref()[head].size(); + ++head; + } else { + break; + } + } + uint32_t range = data_ref()[head][index]; + + // TODO: fetch from RangesUnion[] + const auto code = CommonCode::unsafe_create(head << 32 | range); + return std::bit_cast(code); +} + +// ----------------------------------------------------------------------------------------- // + +PyCases PyCases::from(RangesUnion &&data) noexcept { + return PyCases(std::move(data)); +} + +PyCases PyCases::from_ref(const RangesUnion &data) noexcept { + return PyCases(data); +} + +const RangesUnion& PyCases::data_ref() const noexcept { + return std::visit([](T &&arg) -> const RangesUnion& { + if constexpr (std::is_same_v, RangesUnion>) { + return arg; + } else { + return arg.get(); + } + }, data_); +} + +PyCases::PyCases(RangesUnion &&data) : data_(std::move(data)) {} +PyCases::PyCases(const RangesUnion &data) : data_(std::cref(data)) {} + +// ----------------------------------------------------------------------------------------- // diff --git a/src/core_ffi/py_ffi/wrapper/py_cases.cc b/src/core_ffi/py_ffi/wrapper/py_cases.cc deleted file mode 100644 index 6b56ff9..0000000 --- a/src/core_ffi/py_ffi/wrapper/py_cases.cc +++ /dev/null @@ -1,44 +0,0 @@ -#include - -#include "include/py_cases.h" - -namespace py = pybind11; - -using klotski::ffi::PyCases; -using klotski::ffi::PyCasesIter; -using klotski::ffi::PyCommonCode; - -using klotski::cases::RangesUnion; - -size_t PyCases::size() const { - size_t num = 0; - for (const auto &x : ref()) { - num += x.size(); - } - return num; -} - -//PyCases PyCases::from(RangesUnion &&data) { -// return PyCases(std::move(data)); -//} -// -//PyCases PyCases::from_ref(const RangesUnion &data) { -// return PyCases(data); -//} -// -//const RangesUnion& PyCases::data_ref() const { -// return data_; -//} - -PyCommonCode PyCasesIter::next() { - while (head_ < 16) { - const auto &ranges = data_.ref()[head_]; - if (index_ < ranges.size()) { - auto code = (head_ << 32) | ranges[index_++]; - return std::bit_cast(code); - } - ++head_; - index_ = 0; - } - throw py::stop_iteration(); -}