Skip to content

Commit 832fbf0

Browse files
authored
kj-rs/convert.h for kj-rust object conversion (#39)
Improved iteration of https://github.com/cloudflare/workerd/blob/main/src/rust/cxx-integration/cxx-bridge.h Main difference: introduction of `from<Rust>` and `from<RustCopy>
1 parent 33a3529 commit 832fbf0

File tree

6 files changed

+543
-15
lines changed

6 files changed

+543
-15
lines changed

MODULE.bazel.lock

Lines changed: 11 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

capnp_cpp.bzl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
22

33

4-
URL = "https://github.com/capnproto/capnproto/tarball/fd3001e9ae9c8efaa007518901711ead5584167a"
5-
STRIP_PREFIX = "capnproto-capnproto-fd3001e/c++"
6-
SHA256 = "ce4d082c20f2df051d718a3a72fc6029e84c589c87bc93b8cfeb79522577e822"
4+
URL = "https://github.com/capnproto/capnproto/tarball/5b8cae919e37898d47290e5edd777e1aa6be802a"
5+
STRIP_PREFIX = "capnproto-capnproto-5b8cae9/c++"
6+
SHA256 = "bb508a1762ca4af7b01162c38a2153e4b77595f612aeb27d2b560cc16302bf14"
77
TYPE = "tgz"
8-
COMMIT = "fd3001e9ae9c8efaa007518901711ead5584167a"
8+
COMMIT = "5b8cae919e37898d47290e5edd777e1aa6be802a"
99

1010
def _capnp_cpp(ctx):
1111
http_archive(

kj-rs/convert.h

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
#pragma once
2+
3+
// kj-rs/convert.h - Rust/C++ Interoperability Utilities for KJ
4+
//
5+
// This header provides seamless integration between Rust types and KJ (Cap'n Proto's C++ library)
6+
// types, enabling efficient and safe data exchange across the language boundary.
7+
//
8+
// ============================================================================
9+
// USAGE OVERVIEW
10+
// ============================================================================
11+
//
12+
// Converting C++ kj arrays, strings, etc to Rust:
13+
// - kjObject.as<Rust>() - creates zero-copy read-only Rust view
14+
// - kjObject.as<RustMutable>() - creates zero-copy mutable Rust view
15+
// - kjObject.as<RustCopy>() - creates owned Rust copy
16+
//
17+
// Converting Rust to C++ kj objects:
18+
// - from<Rust>(rustObject) - creates zero-copy C++ view
19+
// - from<RustCopy>(rustObject) - creates owned C++ copy
20+
// - kj::str(rustString) - automatic conversion (via KJ_STRINGIFY)
21+
// - kj::hashCode(rustString) - automatic hash computation (via KJ_HASHCODE)
22+
//
23+
// ============================================================================
24+
// CONVERSION FUNCTIONS
25+
// ============================================================================
26+
//
27+
// Zero-copy conversions from Rust to C++:
28+
// - from<Rust>(rust::Vec<T>) -> kj::ArrayPtr<const T>
29+
// - from<Rust>(rust::Slice<T>) -> kj::ArrayPtr<T>
30+
// - from<Rust>(rust::String) -> kj::ArrayPtr<const char>
31+
// - from<Rust>(rust::str) -> kj::ArrayPtr<const char>
32+
//
33+
// Owned conversions from Rust to C++:
34+
// - from<RustCopy>(rust::Slice<rust::str>) -> kj::Array<kj::String>
35+
// - from<RustCopy>(rust::Vec<rust::String>) -> kj::Array<kj::String>
36+
//
37+
// Zero-copy conversions from C++ to Rust (read-only):
38+
// - kjArray.as<Rust>() -> rust::Slice<const T>
39+
// - kjString.as<Rust>() -> rust::String
40+
// - kjStringPtr.as<Rust>() -> rust::str
41+
// - kjConstString.as<Rust>() -> rust::str
42+
//
43+
// Zero-copy conversions from C++ to Rust (mutable):
44+
// - kjArray.as<RustMutable>() -> rust::Slice<T>
45+
// - kjArrayPtr.as<RustMutable>() -> rust::Slice<T>
46+
//
47+
// Owned conversions from C++ to Rust (copying):
48+
// - kjStringPtr.as<RustCopy>() -> rust::String
49+
// - kjConstString.as<RustCopy>() -> rust::String
50+
// - kjArrayPtr.as<RustCopy>() -> rust::Vec<T>
51+
//
52+
// Automatic conversions (via ADL):
53+
// - kj::str(rust::String) - uses KJ_STRINGIFY for seamless string conversion
54+
// - kj::hashCode(rust::String) - uses KJ_HASHCODE for hash computation
55+
//
56+
// ============================================================================
57+
// EXAMPLES
58+
// ============================================================================
59+
//
60+
// Basic usage patterns:
61+
//
62+
// // Convert Rust to C++:
63+
// kj::ArrayPtr<const int> cppView = from<Rust>(rustVec);
64+
//
65+
// // Convert C++ to Rust (read-only):
66+
// rust::Slice<const int> rustView = cppArray.as<Rust>();
67+
//
68+
// // Convert C++ to Rust (mutable):
69+
// rust::Slice<int> rustMutableView = cppArray.as<RustMutable>();
70+
//
71+
// // Convert C++ to Rust (copying):
72+
// rust::String rustOwnedStr = cppStr.as<RustCopy>();
73+
//
74+
// // Automatic string conversion:
75+
// kj::String cppStr = kj::str(rustStr); // via KJ_STRINGIFY
76+
//
77+
78+
#include <rust/cxx.h>
79+
80+
#include <kj/common.h>
81+
#include <kj/hash.h>
82+
#include <kj/string.h>
83+
84+
namespace rust {
85+
86+
// Automatic KJ Integration Functions - enable ADL for kj::str() and kj::hashCode()
87+
88+
/// Converts rust::String to kj::ArrayPtr - called by kj::str(rustString)
89+
inline auto KJ_STRINGIFY(const ::rust::String& str) {
90+
// HACK: rust::String is not null-terminated, so we use kj::ArrayPtr instead
91+
// which usually acts like kj::StringPtr but does not rely on null
92+
// termination.
93+
return kj::ArrayPtr<const char>(str.data(), str.size());
94+
}
95+
96+
/// Converts rust::str to kj::ArrayPtr - called by kj::str(rustStr)
97+
inline auto KJ_STRINGIFY(const ::rust::str& str) {
98+
// HACK: rust::str is not null-terminated, so we use kj::ArrayPtr instead
99+
// which usually acts like kj::StringPtr but does not rely on null
100+
// termination.
101+
return kj::ArrayPtr<const char>(str.data(), str.size());
102+
}
103+
104+
/// Hash code for rust::String - called by kj::hashCode(rustString)
105+
inline auto KJ_HASHCODE(const ::rust::String& str) {
106+
return kj::hashCode(kj::toCharSequence(str));
107+
}
108+
109+
/// Hash code for rust::str - called by kj::hashCode(rustStr)
110+
inline auto KJ_HASHCODE(const ::rust::str& str) {
111+
return kj::hashCode(kj::toCharSequence(str));
112+
}
113+
114+
} // namespace rust
115+
116+
namespace kj_rs {
117+
118+
// KJ to Rust conversion utilities with different ownership semantics
119+
120+
/// Template function for nicer syntax: from<Rust>(rustObject) instead of fromRust(rustObject)
121+
template <typename T, typename U>
122+
inline auto from(U&& rustObject) {
123+
return T::into(std::forward<U>(rustObject));
124+
}
125+
126+
/// Zero-copy read-only Rust views: kjObject.as<Rust>() and from<Rust>(kjObject)
127+
struct Rust {
128+
/// kjArrayPtr.as<Rust>() - via Rust::from(&kjArrayPtr)
129+
template <typename T>
130+
static ::rust::Slice<const T> from(const kj::ArrayPtr<T>* arr) {
131+
return ::rust::Slice<const T>(arr->begin(), arr->size());
132+
}
133+
134+
/// kjArray.as<Rust>() - via Rust::from(&kjArray)
135+
template <typename T>
136+
static ::rust::Slice<const T> from(const kj::Array<T>* arr) {
137+
return ::rust::Slice<const T>(arr->begin(), arr->size());
138+
}
139+
140+
/// kjString.as<Rust>() - via Rust::from(&kjString)
141+
static ::rust::String from(const kj::String* str) {
142+
return ::rust::String(str->begin(), str->size());
143+
}
144+
145+
/// kjStringPtr.as<Rust>() - via Rust::from(&kjStringPtr)
146+
static ::rust::Str from(const kj::StringPtr* str) {
147+
return ::rust::Str(str->begin(), str->size());
148+
}
149+
150+
/// kjConstString.as<Rust>() - via Rust::from(&kjConstString)
151+
static ::rust::Str from(const kj::ConstString* str) {
152+
return ::rust::Str(str->begin(), str->size());
153+
}
154+
155+
// into() methods for from<Rust>(rustObject) - converting Rust to KJ
156+
157+
/// from<Rust>(rustVec) - Zero-copy read-only view
158+
template <typename T>
159+
static kj::ArrayPtr<const T> into(const ::rust::Vec<T>& vec) {
160+
return kj::ArrayPtr<const T>(vec.data(), vec.size());
161+
}
162+
163+
/// from<Rust>(rustSlice) - Zero-copy slice view
164+
template <typename T>
165+
static kj::ArrayPtr<T> into(const ::rust::Slice<T>& slice) {
166+
return kj::ArrayPtr<T>(slice.data(), slice.size());
167+
}
168+
169+
/// from<Rust>(rustString) - Zero-copy string chars (not null-terminated)
170+
static kj::ArrayPtr<const char> into(const ::rust::String& str) {
171+
return kj::ArrayPtr<const char>(str.data(), str.size());
172+
}
173+
174+
/// from<Rust>(rustStr) - Zero-copy string slice chars (not null-terminated)
175+
static kj::ArrayPtr<const char> into(const ::rust::Str& str) {
176+
return kj::ArrayPtr<const char>(str.data(), str.size());
177+
}
178+
};
179+
180+
/// Owned Rust copies: kjObject.as<RustCopy>() and from<RustCopy>(kjObject)
181+
struct RustCopy {
182+
/// kjStringPtr.as<RustCopy>() - via RustCopy::from(&kjStringPtr)
183+
static ::rust::String from(const kj::StringPtr* str) {
184+
return ::rust::String(str->begin(), str->size());
185+
}
186+
187+
/// kjConstString.as<RustCopy>() - via RustCopy::from(&kjConstString)
188+
static ::rust::String from(const kj::ConstString* str) {
189+
return ::rust::String(str->begin(), str->size());
190+
}
191+
192+
/// kjArrayPtr.as<RustCopy>() - via RustCopy::from(&kjArrayPtr)
193+
template <typename T>
194+
static ::rust::Vec<T> from(kj::ArrayPtr<const T>* arr) {
195+
::rust::Vec<T> result;
196+
result.reserve(arr->size());
197+
for (auto& t: *arr) {
198+
result.push_back(t);
199+
}
200+
return result;
201+
}
202+
203+
/// from<RustCopy>(rustSliceOfStrs) - Copy slice of strs to null-terminated KJ strings
204+
static kj::Array<kj::String> into(::rust::Slice<::rust::str> slice) {
205+
auto res = kj::heapArrayBuilder<kj::String>(slice.size());
206+
for (auto& entry: slice) {
207+
res.add(kj::str(entry));
208+
}
209+
return res.finish();
210+
}
211+
212+
/// from<RustCopy>(rustVecOfStrings) - Copy string vector to null-terminated KJ strings
213+
static kj::Array<kj::String> into(const ::rust::Vec<::rust::String>& vec) {
214+
auto res = kj::heapArrayBuilder<kj::String>(vec.size());
215+
for (auto& entry: vec) {
216+
res.add(kj::str(entry));
217+
}
218+
return res.finish();
219+
}
220+
};
221+
222+
/// Mutable Rust views: kjObject.as<RustMutable>() and from<RustMutable>(kjObject)
223+
struct RustMutable {
224+
/// kjArrayPtr.as<RustMutable>() - via RustMutable::from(&kjArrayPtr)
225+
template <typename T>
226+
static ::rust::Slice<T> from(kj::ArrayPtr<T>* arr) {
227+
return ::rust::Slice<T>(arr->begin(), arr->size());
228+
}
229+
230+
/// kjArray.as<RustMutable>() - via RustMutable::from(&kjArray)
231+
template <typename T>
232+
static ::rust::Slice<T> from(kj::Array<T>* arr) {
233+
return ::rust::Slice<T>(arr->begin(), arr->size());
234+
}
235+
};
236+
237+
} // namespace kj_rs

kj-rs/kj-rs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
#pragma once
22

3+
// KJ-C++ conversion utilities
4+
#include "kj-rs/convert.h"
5+
// Rust futures support
36
#include "kj-rs/future.h"
7+
// KJ promises support
48
#include "kj-rs/promise.h"

kj-rs/tests/BUILD.bazel

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,20 @@ cc_test(
8585
"@capnp-cpp//src/kj:kj-test",
8686
],
8787
)
88+
89+
cc_test(
90+
name = "kj-test",
91+
size = "small",
92+
srcs = [
93+
"convert.c++",
94+
],
95+
linkstatic = select({
96+
"@platforms//os:windows": True,
97+
"//conditions:default": False,
98+
}),
99+
deps = [
100+
"//kj-rs",
101+
"//third-party:runtime",
102+
"@capnp-cpp//src/kj:kj-test",
103+
],
104+
)

0 commit comments

Comments
 (0)