@@ -200,75 +200,64 @@ JSObject *PromiseRejectedWithPendingError(JSContext *cx) {
200
200
return promise;
201
201
}
202
202
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 ;
220
204
221
- // For realloc below.
222
- char *new_buf ;
205
+ struct ReadChunk {
206
+ using UniquePtr = std::unique_ptr< char [], void (*)( void *)> ;
223
207
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 ;
226
225
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)) {
229
229
HANDLE_ERROR (cx, err);
230
- JS_free (cx, buf);
231
- return nullptr ;
230
+ return {nullptr , 0 };
232
231
}
233
232
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 ) {
243
234
break ;
244
235
}
245
236
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) {
252
250
JS_ReportOutOfMemory (cx);
253
- return nullptr ;
251
+ return { nullptr , 0 } ;
254
252
}
255
- buf = new_buf;
256
253
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
+ }
265
258
}
266
- buf = new_buf;
267
-
268
- buf[offset] = ' \0 ' ;
269
- *nwritten = offset;
270
259
271
- return buf;
260
+ return { std::move ( buf), bytes_read} ;
272
261
}
273
262
274
263
/* *
@@ -999,9 +988,8 @@ bool consume_content_stream_for_bodyAll(JSContext *cx, HandleObject self, Handle
999
988
bool consume_body_handle_for_bodyAll (JSContext *cx, HandleObject self, HandleValue body_parser,
1000
989
CallArgs args) {
1001
990
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);
1005
993
if (!buf) {
1006
994
RootedObject result_promise (cx);
1007
995
result_promise = &JS::GetReservedSlot (self, Slots::BodyAllPromise).toObject ();
0 commit comments