Skip to content

Commit 576a6b3

Browse files
authored
chore: C++ interface to host calls (#382)
1 parent 6ca8670 commit 576a6b3

File tree

5 files changed

+286
-108
lines changed

5 files changed

+286
-108
lines changed

c-dependencies/js-compute-runtime/builtins/object-store.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "builtin.h"
1818
#include "builtins/native-stream-source.h"
1919
#include "builtins/url.h"
20+
#include "host_api.h"
2021
#include "host_call.h"
2122
#include "js-compute-builtins.h"
2223

@@ -325,31 +326,32 @@ bool put(JSContext *cx, unsigned argc, JS::Value *vp) {
325326
buf = text.get();
326327
}
327328

328-
fastly_body_handle_t body_handle = INVALID_HANDLE;
329-
fastly_error_t err;
330-
if (!xqd_fastly_http_body_new(&body_handle, &err)) {
331-
HANDLE_ERROR(cx, err);
329+
auto make_res = HttpBody::make();
330+
if (auto *err = make_res.to_err()) {
331+
HANDLE_ERROR(cx, *err);
332332
return ReturnPromiseRejectedWithPendingError(cx, args);
333333
}
334334

335-
if (body_handle == INVALID_HANDLE) {
335+
auto body = make_res.unwrap();
336+
if (!body.valid()) {
336337
return ReturnPromiseRejectedWithPendingError(cx, args);
337338
}
338339

339-
bool ok = write_to_body_all(body_handle, buf, length, &err);
340+
auto write_res = body.write_all(reinterpret_cast<uint8_t *>(buf), length);
340341

341342
// Ensure that the NoGC is reset, so throwing an error in HANDLE_ERROR
342343
// succeeds.
343344
if (maybeNoGC.isSome()) {
344345
maybeNoGC.reset();
345346
}
346347

347-
if (!ok) {
348-
HANDLE_ERROR(cx, err);
348+
if (auto *err = write_res.to_err()) {
349+
HANDLE_ERROR(cx, *err);
349350
return ReturnPromiseRejectedWithPendingError(cx, args);
350351
}
351352

352-
if (!xqd_fastly_object_store_insert(object_store_handle(self), &key_str, body_handle, &err)) {
353+
fastly_error_t err;
354+
if (!xqd_fastly_object_store_insert(object_store_handle(self), &key_str, body.handle, &err)) {
353355
// Ensure that we throw an exception for all unexpected host errors.
354356
HANDLE_ERROR(cx, err);
355357
return RejectPromiseWithPendingError(cx, result_promise);
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#include "host_api.h"
2+
#include "xqd_world.h"
3+
#include "xqd_world_adapter.h"
4+
5+
Result<HttpBody> HttpBody::make() {
6+
Result<HttpBody> res;
7+
8+
fastly_body_handle_t handle;
9+
fastly_error_t err;
10+
if (!xqd_fastly_http_body_new(&handle, &err)) {
11+
res.emplace_err(err);
12+
} else {
13+
res.emplace(handle);
14+
}
15+
16+
return res;
17+
}
18+
19+
Result<HttpBodyChunk> HttpBody::read(uint32_t chunk_size) const {
20+
Result<HttpBodyChunk> res;
21+
22+
fastly_list_u8_t ret;
23+
fastly_error_t err;
24+
if (!xqd_fastly_http_body_read(this->handle, chunk_size, &ret, &err)) {
25+
res.emplace_err(err);
26+
} else {
27+
res.emplace(JS::UniqueChars(reinterpret_cast<char *>(ret.ptr)), ret.len);
28+
}
29+
30+
return res;
31+
}
32+
33+
Result<uint32_t> HttpBody::write(const uint8_t *ptr, size_t len) const {
34+
Result<uint32_t> res;
35+
36+
// The write call doesn't mutate the buffer; the cast is just for the generated xqd api.
37+
fastly_list_u8_t chunk{const_cast<uint8_t *>(ptr), len};
38+
39+
fastly_error_t err;
40+
uint32_t written;
41+
if (!xqd_fastly_http_body_write(this->handle, &chunk, FASTLY_BODY_WRITE_END_BACK, &written,
42+
&err)) {
43+
res.emplace_err(err);
44+
} else {
45+
res.emplace(written);
46+
}
47+
48+
return res;
49+
}
50+
51+
Result<Void> HttpBody::write_all(const uint8_t *ptr, size_t len) const {
52+
while (len > 0) {
53+
auto write_res = this->write(ptr, len);
54+
if (auto *err = write_res.to_err()) {
55+
return Result<Void>::err(*err);
56+
}
57+
58+
auto written = write_res.unwrap();
59+
ptr += written;
60+
len -= std::min(len, static_cast<size_t>(written));
61+
}
62+
63+
return Result<Void>::ok();
64+
}
65+
66+
Result<Void> HttpBody::append(HttpBody other) const {
67+
Result<Void> res;
68+
69+
fastly_error_t err;
70+
if (!xqd_fastly_http_body_append(this->handle, other.handle, &err)) {
71+
res.emplace_err(err);
72+
} else {
73+
res.emplace();
74+
}
75+
76+
return res;
77+
}
78+
79+
Result<Void> HttpBody::close() {
80+
Result<Void> res;
81+
82+
fastly_error_t err;
83+
if (!xqd_fastly_http_body_close(this->handle, &err)) {
84+
res.emplace_err(err);
85+
} else {
86+
res.emplace();
87+
}
88+
89+
return res;
90+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#ifndef JS_COMPUTE_RUNTIME_HOST_API_H
2+
#define JS_COMPUTE_RUNTIME_HOST_API_H
3+
4+
#include <memory>
5+
#include <variant>
6+
7+
#include "allocator.h"
8+
#include "xqd-world/xqd_world.h"
9+
10+
#pragma clang diagnostic push
11+
#pragma clang diagnostic ignored "-Winvalid-offsetof"
12+
#include "js/Utility.h"
13+
#pragma clang diagnostic pop
14+
15+
/// A type to signal that a result produces no value.
16+
struct Void final {};
17+
18+
template <typename T> class Result final {
19+
/// A private wrapper to distinguish `fastly_error_t` in the private variant.
20+
struct Error {
21+
fastly_error_t value;
22+
23+
explicit Error(fastly_error_t value) : value{value} {}
24+
};
25+
26+
std::variant<T, Error> result;
27+
28+
public:
29+
Result() = default;
30+
31+
/// Explicitly construct an error.
32+
static Result err(fastly_error_t err) {
33+
Result res;
34+
res.emplace_err(err);
35+
return res;
36+
}
37+
38+
/// Explicitly construct a successful result.
39+
template <typename... Args> static Result ok(Args &&...args) {
40+
Result res;
41+
res.emplace(std::forward<Args>(args)...);
42+
return res;
43+
}
44+
45+
/// Construct an error in-place.
46+
fastly_error_t &emplace_err(fastly_error_t err) & {
47+
return this->result.template emplace<Error>(err).value;
48+
}
49+
50+
/// Construct a value of T in-place.
51+
template <typename... Args> T &emplace(Args &&...args) {
52+
return this->result.template emplace<T>(std::forward<Args>(args)...);
53+
}
54+
55+
/// True when the result contains an error.
56+
bool is_err() const { return std::holds_alternative<Error>(this->result); }
57+
58+
/// Return a pointer to the error value of this result, if the call failed.
59+
const fastly_error_t *to_err() const {
60+
return reinterpret_cast<const fastly_error_t *>(std::get_if<Error>(&this->result));
61+
}
62+
63+
/// Assume the call was successful, and return a reference to the result.
64+
T &unwrap() { return std::get<T>(this->result); }
65+
};
66+
67+
/// A single chunk read from an HttpBody.
68+
struct HttpBodyChunk {
69+
JS::UniqueChars ptr;
70+
size_t len;
71+
72+
HttpBodyChunk() = default;
73+
HttpBodyChunk(JS::UniqueChars ptr, size_t len) : ptr{std::move(ptr)}, len{len} {}
74+
};
75+
76+
/// A convenience wrapper for the host calls involving http bodies.
77+
class HttpBody final {
78+
public:
79+
static constexpr fastly_body_handle_t invalid = UINT32_MAX - 1;
80+
81+
/// The handle to use when making host calls, initialized to the special invalid value used by
82+
/// executed.
83+
fastly_body_handle_t handle = invalid;
84+
85+
HttpBody() = default;
86+
explicit HttpBody(fastly_body_handle_t handle) : handle{handle} {}
87+
88+
/// Returns true when this body handle is valid.
89+
bool valid() const { return this->handle != invalid; }
90+
91+
/// Make a new body handle.
92+
static Result<HttpBody> make();
93+
94+
/// Read a chunk from this handle.
95+
Result<HttpBodyChunk> read(uint32_t chunk_size) const;
96+
97+
/// Write a chunk to this handle.
98+
Result<uint32_t> write(const uint8_t *bytes, size_t len) const;
99+
100+
/// Writes the given number of bytes from the given buffer to the given handle.
101+
///
102+
/// The host doesn't necessarily write all bytes in any particular call to
103+
/// `write`, so to ensure all bytes are written, we call it in a loop.
104+
Result<Void> write_all(const uint8_t *bytes, size_t len) const;
105+
106+
/// Append another HttpBody to this one.
107+
Result<Void> append(HttpBody other) const;
108+
109+
/// Close this handle, and reset internal state to invalid.
110+
Result<Void> close();
111+
};
112+
113+
#endif

0 commit comments

Comments
 (0)