/* Python file-like <-> std::stream adaptation for BinTools. Upstream's signature takes an io.BytesIO, and the app always passes one (cad_pool and derive.py serialise shapes through BytesIO). Rather than bridge a streambuf that calls back into Python on every chunk, this slurps: read the whole payload first, then hand the kernel a pure C++ stream. Two things fall out of that. It is correct no matter how BinTools seeks inside the stream, and the kernel half touches no Python at all — so the GIL can be released around it, which a callback bridge could not do. The cost is one extra copy of the BREP bytes, which n3xd_ocp.bintools avoids entirely for the pool paths that care. */ #pragma once #include #include namespace nb = nanobind; /// Read a Python file-like object to end of stream. inline std::string ocp_slurp(nb::handle stream) { nb::object data = nb::getattr(stream, "read")(); char *buf = nullptr; Py_ssize_t size = 0; if (PyBytes_AsStringAndSize(data.ptr(), &buf, &size) != 0) throw nb::python_error(); return std::string(buf, (size_t) size); } /// Write a byte payload to a Python file-like object. inline void ocp_spit(nb::handle stream, const std::string &data) { nb::object payload = nb::steal( PyBytes_FromStringAndSize(data.data(), (Py_ssize_t) data.size())); if (!payload.is_valid()) throw nb::python_error(); nb::getattr(stream, "write")(payload); }