diff --git a/crates/cpp/helper-types/wit.h b/crates/cpp/helper-types/wit.h index e643a5262..acad20c37 100644 --- a/crates/cpp/helper-types/wit.h +++ b/crates/cpp/helper-types/wit.h @@ -16,41 +16,11 @@ #include // pair namespace wit { -/// @brief Helper class to map between IDs and resources -/// @tparam R Type of the Resource -template class ResourceTable { - static std::map resources; - -public: - static R *lookup_resource(int32_t id) { - auto result = resources.find(id); - return result == resources.end() ? nullptr : &result->second; - } - static int32_t store_resource(R &&value) { - auto last = resources.rbegin(); - int32_t id = last == resources.rend() ? 0 : last->first + 1; - resources.insert(std::pair(id, std::move(value))); - return id; - } - static std::optional remove_resource(int32_t id) { - auto iter = resources.find(id); - std::optional result; - if (iter != resources.end()) { - result = std::move(iter->second); - resources.erase(iter); - } - return std::move(result); - } -}; - /// @brief Replaces void in the error position of a result struct Void {}; -/// A string in linear memory, freed unconditionally using free -/// -/// A normal C++ string makes no guarantees about where the characters -/// are stored and how this is freed. -class string { +class borrowed_string { +protected: uint8_t const *data_; size_t length; // C++ is horrible! @@ -58,44 +28,18 @@ class string { static uint8_t const* empty_ptr() { return (uint8_t const *)1; } public: - // this constructor is helpful for creating vector - string(string const &b) : string(string::from_view(b.get_view())) {} - string(string &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } - string &operator=(string const &) = delete; - string &operator=(string &&b) { - if (data_ && data_!=empty_ptr()) { - free(const_cast(data_)); - } - data_ = b.data_; - length = b.length; - b.data_ = nullptr; - return *this; - } - string(char const *d, size_t l) : data_((uint8_t const *)d), length(l) {} + borrowed_string(borrowed_string &&b) : data_(b.data_), length(b.length) {} + borrowed_string &operator=(borrowed_string const &) = delete; + borrowed_string(char const *d, size_t l) : data_((uint8_t const *)d), length(l) {} char const *data() const { return (char const *)data_; } size_t size() const { return length; } bool empty() const { return !length; } - ~string() { - if (data_ && data_!=empty_ptr()) { - free(const_cast(data_)); - } - } - // leak the memory - void leak() { data_ = nullptr; } - // typically called by post - static void drop_raw(void *ptr) { free(ptr); } std::string_view get_view() const { return std::string_view((const char *)data_, length); } std::string to_string() const { return std::string((const char *)data_, length); } - static string from_view(std::string_view v) { - if (!v.size()) return string((char const*)empty_ptr(), 0); - char* addr = (char*)malloc(v.size()); - memcpy(addr, v.data(), v.size()); - return string(addr, v.size()); - } char* begin() { return (char*)data_; } @@ -110,58 +54,109 @@ class string { } }; -/// A vector in linear memory, freed unconditionally using free +/// A string in linear memory, freed unconditionally using free /// -/// You can't detach the data memory from a vector, nor create one -/// in a portable way from a buffer and lenght without copying. -template class vector { +/// A normal C++ string makes no guarantees about where the characters +/// are stored and how this is freed. +class string : public borrowed_string { +public: + // this constructor is helpful for creating vector + string(string const &b) : string(string::from_view(b.get_view())) {} + string(string &&b) : borrowed_string(std::move(b)) { b.data_ = nullptr; } + string &operator=(string const &) = delete; + string(char const *d, size_t l) : borrowed_string(d, l) {} + string &operator=(string &&b) { + if (this->data_ && this->data_!=this->empty_ptr()) { + free(const_cast(this->data_)); + } + this->data_ = b.data_; + this->length = b.length; + b.data_ = nullptr; + return *this; + } + // leak the memory + void leak() { data_ = nullptr; } + // typically called by post + static void drop_raw(void *ptr) { free(ptr); } + ~string() { + if (this->data_ && this->data_!=this->empty_ptr()) { + free(const_cast(this->data_)); + } + } + static string from_view(std::string_view v) { + if (!v.size()) return string((char const*)empty_ptr(), 0); + char* addr = (char*)malloc(v.size()); + memcpy(addr, v.data(), v.size()); + return string(addr, v.size()); + } +}; + +template class borrowed_vector { +protected: T *data_; size_t length; static T* empty_ptr() { return (T*)alignof(T); } public: - vector(vector const &) = delete; - vector(vector &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } - vector &operator=(vector const &) = delete; - vector &operator=(vector &&b) { - if (data_ && length>0) { - free(data_); - } + borrowed_vector(borrowed_vector const &) = delete; + borrowed_vector(borrowed_vector &&b) : data_(b.data_), length(b.length) {} + borrowed_vector &operator=(borrowed_vector const &) = delete; + borrowed_vector &operator=(borrowed_vector &&b) { data_ = b.data_; length = b.length; - b.data_ = nullptr; return *this; } - vector(T *d, size_t l) : data_(d), length(l) {} + borrowed_vector(T *d, size_t l) : data_(d), length(l) {} // Rust needs a nonzero pointer here (alignment is typical) - vector() : data_(empty_ptr()), length() {} + borrowed_vector() : data_(empty_ptr()), length() {} T const *data() const { return data_; } T *data() { return data_; } T &operator[](size_t n) { return data_[n]; } T const &operator[](size_t n) const { return data_[n]; } size_t size() const { return length; } bool empty() const { return !length; } + std::span get_view() const { return std::span(data_, length); } + std::span get_const_view() const { return std::span(data_, length); } +}; + +/// A vector in linear memory, freed unconditionally using free +/// +/// You can't detach the data memory from a vector, nor create one +/// in a portable way from a buffer and lenght without copying. +template class vector : public borrowed_vector { +public: + vector(vector const &) = delete; + vector(vector &&b) : borrowed_vector(b.data_, b.length) { b.data_ = nullptr; } + vector(T *d, size_t l) : borrowed_vector(d, l) {} + vector &operator=(vector const &) = delete; + vector &operator=(vector &&b) { + if (this->data_ && this->length>0) { + free(this->data_); + } + this->data_ = b.data_; + this->length = b.length; + b.data_ = nullptr; + return *this; + } ~vector() { - if (data_ && length>0) { - for (unsigned i=0;idata_ && this->length>0) { + for (unsigned i=0;ilength;++i) { this->data_[i].~T(); } + free((void*)this->data_); } } // WARNING: vector contains uninitialized elements static vector allocate(size_t len) { - if (!len) return vector(empty_ptr(), 0); + if (!len) return vector(borrowed_vector::empty_ptr(), 0); return vector((T*)malloc(sizeof(T)*len), len); } void initialize(size_t n, T&& elem) { - new ((void*)(data_+n)) T(std::move(elem)); + new ((void*)(this->data_+n)) T(std::move(elem)); } // leak the memory - T* leak() { T*result = data_; data_ = nullptr; return result; } + T* leak() { T*result = this->data_; this->data_ = nullptr; return result; } // typically called by post - static void drop_raw(void *ptr) { if (ptr!=empty_ptr()) free(ptr); } - std::span get_view() const { return std::span(data_, length); } - std::span get_const_view() const { return std::span(data_, length); } + static void drop_raw(void *ptr) { if (ptr!=borrowed_vector::empty_ptr()) free(ptr); } template static vector from_view(std::span const& a) { auto result = vector::allocate(a.size()); for (uint32_t i=0;i Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let body = self.blocks.pop().unwrap(); let val = format!("_vec{tmp}"); - let ptr = format!("_ptr{tmp}"); + let vres = format!("_res{tmp}"); + let vptr = format!("_vptr{tmp}"); let len = format!("_len{tmp}"); let size = self.r#gen.sizes.size(element); - self.push_str(&format!("auto&& {} = {};\n", val, operands[0])); + self.push_str(&format!("auto&& {val} = {};\n", operands[0])); + self.push_str(&format!("auto {len} = (size_t)({val}.size());\n")); + let subvar = match self.variant { + AbiVariant::GuestImport => SubFlavor::Borrowed, + AbiVariant::GuestExport => todo!(), + AbiVariant::GuestImportAsync => todo!(), + AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), + }; + let tpe = + self.r#gen + .type_name(element, &self.namespace, Flavor::CanonicalAbi(subvar)); self.push_str(&format!( - "auto {} = ({})({}.data());\n", - ptr, - self.r#gen.r#gen.opts.ptr_type(), - val + "auto {vres} = wit::vector<{tpe}>::allocate({len});\n" + )); + self.push_str(&format!( + "auto {vptr} = ({})({vres}.data());\n", + self.r#gen.r#gen.opts.ptr_type() )); - self.push_str(&format!("auto {len} = (size_t)({val}.size());\n")); self.push_str(&format!("for (size_t i = 0; i < {len}; ++i) {{\n")); self.push_str(&format!( - "auto _base = {ptr} + i * {size};\n", + "auto _base = {vptr} + i * {size};\n", size = size.format(POINTER_SIZE_EXPRESSION) )); self.push_str(&format!("auto&& _iter_elem = {val}[i];\n")); self.push_str(&format!("{}\n", body.0)); self.push_str("}\n"); if realloc.is_none() { - results.push(ptr); + results.push(vptr); } else { - uwriteln!(self.src, "{}.leak();\n", operands[0]); - results.push(ptr); + // free the vector storage, but don't destroy elements + uwriteln!( + self.src, + "wit::vector<{tpe}>::drop_raw({}.leak());\n", + operands[0] + ); + uwriteln!(self.src, "{vres}.leak();\n"); + results.push(vptr); } results.push(len); }