Skip to content

Commit d736421

Browse files
authored
Refactor read_from_handle_all (#379)
1 parent 885e398 commit d736421

File tree

1 file changed

+46
-58
lines changed

1 file changed

+46
-58
lines changed

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

Lines changed: 46 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -200,75 +200,64 @@ JSObject *PromiseRejectedWithPendingError(JSContext *cx) {
200200
return promise;
201201
}
202202

203-
#define HANDLE_READ_CHUNK_SIZE 8192
204-
205-
template <auto op>
206-
static char *read_from_handle_all(JSContext *cx, uint32_t handle, size_t *nwritten,
207-
bool read_until_zero) {
208-
// TODO(performance): investigate passing a size hint in situations where we might know
209-
// the final size, e.g. via the `content-length` header.
210-
// https://github.com/fastly/js-compute-runtime/issues/216
211-
size_t buf_size = HANDLE_READ_CHUNK_SIZE;
212-
213-
// TODO(performance): make use of malloc slack.
214-
// https://github.com/fastly/js-compute-runtime/issues/217
215-
char *buf = static_cast<char *>(JS_malloc(cx, buf_size));
216-
if (!buf) {
217-
JS_ReportOutOfMemory(cx);
218-
return nullptr;
219-
}
203+
constexpr size_t HANDLE_READ_CHUNK_SIZE = 8192;
220204

221-
// For realloc below.
222-
char *new_buf;
205+
struct ReadChunk {
206+
using UniquePtr = std::unique_ptr<char[], void (*)(void *)>;
223207

224-
size_t offset = 0;
225-
fastly_error_t err;
208+
UniquePtr ptr;
209+
size_t len;
210+
211+
explicit ReadChunk(fastly_list_u8_t chunk)
212+
: ptr(reinterpret_cast<char *>(chunk.ptr), cabi_free), len{chunk.len} {}
213+
};
214+
215+
struct ReadResult {
216+
UniqueChars buffer;
217+
size_t length;
218+
};
219+
220+
// Returns a UniqueChars and the length of that string. The UniqueChars value is not
221+
// null-terminated.
222+
ReadResult read_from_handle_all(JSContext *cx, uint32_t handle) {
223+
std::vector<ReadChunk> chunks;
224+
size_t bytes_read = 0;
226225
while (true) {
227-
fastly_list_u8_t out_list;
228-
if (!op(handle, HANDLE_READ_CHUNK_SIZE, &out_list, &err)) {
226+
fastly_list_u8_t chunk;
227+
fastly_error_t err;
228+
if (!xqd_fastly_http_body_read(handle, HANDLE_READ_CHUNK_SIZE, &chunk, &err)) {
229229
HANDLE_ERROR(cx, err);
230-
JS_free(cx, buf);
231-
return nullptr;
230+
return {nullptr, 0};
232231
}
233232

234-
// copy the allocated discontiguous buffer into our contiguous buffer
235-
// TODO(performance): a cabi_realloc hack should be possible in the
236-
// component API to ensure continguous allocation into the contiguous
237-
// buffer in the first place
238-
memcpy(buf + offset, out_list.ptr, out_list.len);
239-
JS_free(cx, out_list.ptr);
240-
241-
offset += out_list.len;
242-
if (out_list.len == 0 || (!read_until_zero && out_list.len < HANDLE_READ_CHUNK_SIZE)) {
233+
if (chunk.len == 0) {
243234
break;
244235
}
245236

246-
// TODO(performance): make use of malloc slack, and use a smarter buffer growth strategy.
247-
// https://github.com/fastly/js-compute-runtime/issues/217
248-
size_t new_size = buf_size + HANDLE_READ_CHUNK_SIZE;
249-
new_buf = static_cast<char *>(JS_realloc(cx, buf, buf_size, new_size));
250-
if (!new_buf) {
251-
JS_free(cx, buf);
237+
bytes_read += chunk.len;
238+
chunks.emplace_back(ReadChunk{chunk});
239+
}
240+
241+
UniqueChars buf;
242+
if (chunks.size() == 1) {
243+
// If there was only one chunk read, reuse that allocation if possible.
244+
auto &chunk = chunks.back();
245+
buf = UniqueChars(chunk.ptr.release());
246+
} else {
247+
// If there wasn't exactly one chunk read, we'll need to allocate a buffer to store the results.
248+
buf.reset(static_cast<char *>(JS_string_malloc(cx, bytes_read)));
249+
if (!buf) {
252250
JS_ReportOutOfMemory(cx);
253-
return nullptr;
251+
return {nullptr, 0};
254252
}
255-
buf = new_buf;
256253

257-
buf_size += HANDLE_READ_CHUNK_SIZE;
258-
}
259-
260-
new_buf = static_cast<char *>(JS_realloc(cx, buf, buf_size, offset + 1));
261-
if (!buf) {
262-
JS_free(cx, buf);
263-
JS_ReportOutOfMemory(cx);
264-
return nullptr;
254+
char *end = buf.get();
255+
for (auto &chunk : chunks) {
256+
end = std::copy(chunk.ptr.get(), chunk.ptr.get() + chunk.len, end);
257+
}
265258
}
266-
buf = new_buf;
267-
268-
buf[offset] = '\0';
269-
*nwritten = offset;
270259

271-
return buf;
260+
return {std::move(buf), bytes_read};
272261
}
273262

274263
/**
@@ -999,9 +988,8 @@ bool consume_content_stream_for_bodyAll(JSContext *cx, HandleObject self, Handle
999988
bool consume_body_handle_for_bodyAll(JSContext *cx, HandleObject self, HandleValue body_parser,
1000989
CallArgs args) {
1001990
fastly_body_handle_t body = body_handle(self);
1002-
auto parse_body = (ParseBodyCB *)body_parser.toPrivate();
1003-
size_t bytes_read;
1004-
UniqueChars buf(read_from_handle_all<xqd_fastly_http_body_read>(cx, body, &bytes_read, true));
991+
auto *parse_body = reinterpret_cast<ParseBodyCB *>(body_parser.toPrivate());
992+
auto [buf, bytes_read] = read_from_handle_all(cx, body);
1005993
if (!buf) {
1006994
RootedObject result_promise(cx);
1007995
result_promise = &JS::GetReservedSlot(self, Slots::BodyAllPromise).toObject();

0 commit comments

Comments
 (0)