Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 79 additions & 84 deletions crates/cpp/helper-types/wit.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,86 +16,30 @@
#include <utility> // pair

namespace wit {
/// @brief Helper class to map between IDs and resources
/// @tparam R Type of the Resource
template <class R> class ResourceTable {
static std::map<int32_t, R> 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<int32_t, R>(id, std::move(value)));
return id;
}
static std::optional<R> remove_resource(int32_t id) {
auto iter = resources.find(id);
std::optional<R> 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!
//constexpr uint8_t const *const empty_ptr = (uint8_t const *)1;
static uint8_t const* empty_ptr() { return (uint8_t const *)1; }

public:
// this constructor is helpful for creating vector<string>
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<uint8_t *>(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<uint8_t *>(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_;
}
Expand All @@ -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 T> 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(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<uint8_t *>(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<uint8_t *>(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 T> 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<T> get_view() const { return std::span<T>(data_, length); }
std::span<const T> get_const_view() const { return std::span<const T>(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 T> class vector : public borrowed_vector<T> {
public:
vector(vector const &) = delete;
vector(vector &&b) : borrowed_vector<T>(b.data_, b.length) { b.data_ = nullptr; }
vector(T *d, size_t l) : borrowed_vector<T>(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;i<length;++i) { data_[i].~T(); }
free((void*)data_);
if (this->data_ && this->length>0) {
for (unsigned i=0;i<this->length;++i) { this->data_[i].~T(); }
free((void*)this->data_);
}
}
// WARNING: vector contains uninitialized elements
static vector<T> allocate(size_t len) {
if (!len) return vector<T>(empty_ptr(), 0);
if (!len) return vector<T>(borrowed_vector<T>::empty_ptr(), 0);
return vector<T>((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<T> get_view() const { return std::span<T>(data_, length); }
std::span<const T> get_const_view() const { return std::span<const T>(data_, length); }
static void drop_raw(void *ptr) { if (ptr!=borrowed_vector<T>::empty_ptr()) free(ptr); }
template <class U> static vector<T> from_view(std::span<U> const& a) {
auto result = vector<T>::allocate(a.size());
for (uint32_t i=0;i<a.size();++i) {
Expand Down
47 changes: 36 additions & 11 deletions crates/cpp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,19 @@ pub const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)";

type CppType = String;

#[derive(Clone, Copy, Debug)]
enum SubFlavor {
Owned,
Borrowed,
}

#[derive(Clone, Copy, Debug)]
enum Flavor {
Argument(AbiVariant),
Result(AbiVariant),
InStruct,
BorrowedArgument,
CanonicalAbi(SubFlavor),
}

#[derive(Default)]
Expand Down Expand Up @@ -2619,30 +2626,48 @@ impl<'a, 'b> 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);
}
Expand Down
Loading