Skip to content

Commit

Permalink
py: Optimize functions that take Byml
Browse files Browse the repository at this point in the history
Avoid copies by borrowing (temporarily moving) arrays and hashes.
  • Loading branch information
leoetlino committed Feb 15, 2020
1 parent 3f1b5f6 commit 93a0149
Showing 1 changed file with 38 additions and 10 deletions.
48 changes: 38 additions & 10 deletions py/py_byml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* along with syaz0. If not, see <http://www.gnu.org/licenses/>.
*/

#include <functional>
#include <map>
#include <nonstd/span.h>
#include <type_traits>
Expand All @@ -26,6 +27,7 @@
#include <pybind11/pybind11.h>

#include <oead/byml.h>
#include <oead/util/scope_guard.h>
#include "main.h"

OEAD_MAKE_OPAQUE("Array", oead::Byml::Array);
Expand Down Expand Up @@ -55,24 +57,50 @@ struct type_caster<oead::Byml> {
} // namespace pybind11::detail

namespace oead::bind {
/// Wraps a callable to avoid expensive conversions to Byml; instead, we detect
/// whether the first parameter is a Byml::Array or a Byml::Hash and if so we
/// borrow the value and move-construct a temporary Byml in order to avoid copies.
template <typename... Ts, typename Callable>
static auto BorrowByml(Callable&& callable) {
return [&, callable](py::handle handle, Ts&&... args) {
Byml obj;
const auto invoke = [&, callable](auto&& value) {
obj = std::move(value);
return std::invoke(callable, obj, std::forward<Ts>(args)...);
};
if (py::isinstance<Byml::Array>(handle)) {
auto& ref = handle.cast<Byml::Array&>();
util::ScopeGuard guard{[&] { ref = std::move(obj.Get<Byml::Type::Array>()); }};
return invoke(std::move(ref));
}
if (py::isinstance<Byml::Hash>(handle)) {
auto& ref = handle.cast<Byml::Hash&>();
util::ScopeGuard guard{[&] { ref = std::move(obj.Get<Byml::Type::Hash>()); }};
return invoke(std::move(ref));
}
return invoke(handle.cast<Byml>());
};
}

void BindByml(py::module& parent) {
auto m = parent.def_submodule("byml");
m.def(
"from_binary", [](py::buffer b) { return Byml::FromBinary(PyBufferToSpan(b)); }, "buffer"_a,
py::return_value_policy::move, ":return: An Array or a Hash.");
m.def("from_text", &Byml::FromText, "yml_text"_a, py::return_value_policy::move,
":return: An Array or a Hash.");
m.def("to_binary", &Byml::ToBinary, "data"_a, "big_endian"_a, "version"_a = 2);
m.def("to_text", &Byml::ToText, "data"_a);
m.def("to_binary", BorrowByml<bool, int>(&Byml::ToBinary), "data"_a, "big_endian"_a,
"version"_a = 2);
m.def("to_text", BorrowByml(&Byml::ToText), "data"_a);

m.def("get_bool", &Byml::GetBool, "data"_a);
m.def("get_double", &Byml::GetDouble, "data"_a);
m.def("get_float", &Byml::GetFloat, "data"_a);
m.def("get_int", &Byml::GetInt, "data"_a);
m.def("get_int64", &Byml::GetInt64, "data"_a);
m.def("get_string", &Byml::GetString, "data"_a);
m.def("get_uint", &Byml::GetUInt, "data"_a);
m.def("get_uint64", &Byml::GetUInt64, "data"_a);
m.def("get_bool", BorrowByml(&Byml::GetBool), "data"_a);
m.def("get_double", BorrowByml(&Byml::GetDouble), "data"_a);
m.def("get_float", BorrowByml(&Byml::GetFloat), "data"_a);
m.def("get_int", BorrowByml(&Byml::GetInt), "data"_a);
m.def("get_int64", BorrowByml(&Byml::GetInt64), "data"_a);
m.def("get_string", BorrowByml(&Byml::GetString), "data"_a);
m.def("get_uint", BorrowByml(&Byml::GetUInt), "data"_a);
m.def("get_uint64", BorrowByml(&Byml::GetUInt64), "data"_a);

BindVector<Byml::Array>(m, "Array");
BindMap<Byml::Hash>(m, "Hash");
Expand Down

0 comments on commit 93a0149

Please sign in to comment.