Skip to content

Commit 7f3f0a0

Browse files
authored
chore: Move encoding operations to core/encode.h, and make them return HostString (#591)
1 parent 5debe80 commit 7f3f0a0

32 files changed

+390
-388
lines changed

runtime/js-compute-runtime/builtin.cpp

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,3 @@ std::optional<std::span<uint8_t>> value_to_buffer(JSContext *cx, JS::HandleValue
3030

3131
return std::span(data, len);
3232
}
33-
34-
JS::UniqueChars encode(JSContext *cx, JS::HandleString str, size_t *encoded_len) {
35-
JS::UniqueChars text = JS_EncodeStringToUTF8(cx, str);
36-
if (!text)
37-
return nullptr;
38-
39-
// This shouldn't fail, since the encode operation ensured `str` is linear.
40-
JSLinearString *linear = JS_EnsureLinearString(cx, str);
41-
*encoded_len = JS::GetDeflatedUTF8StringLength(linear);
42-
return text;
43-
}
44-
45-
JS::UniqueChars encode(JSContext *cx, JS::HandleValue val, size_t *encoded_len) {
46-
JS::RootedString str(cx, JS::ToString(cx, val));
47-
if (!str)
48-
return nullptr;
49-
50-
return encode(cx, str, encoded_len);
51-
}
52-
53-
jsurl::SpecString encode(JSContext *cx, JS::HandleValue val) {
54-
jsurl::SpecString slice(nullptr, 0, 0);
55-
auto chars = encode(cx, val, &slice.len);
56-
if (!chars)
57-
return slice;
58-
slice.data = (uint8_t *)chars.release();
59-
slice.cap = slice.len;
60-
return slice;
61-
}

runtime/js-compute-runtime/builtin.h

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,6 @@
2222

2323
std::optional<std::span<uint8_t>> value_to_buffer(JSContext *cx, JS::HandleValue val,
2424
const char *val_desc);
25-
26-
// TODO(performance): introduce a version that writes into an existing buffer, and use that
27-
// with the hostcall buffer where possible.
28-
// https://github.com/fastly/js-compute-runtime/issues/215
29-
JS::UniqueChars encode(JSContext *cx, JS::HandleString str, size_t *encoded_len);
30-
31-
JS::UniqueChars encode(JSContext *cx, JS::HandleValue val, size_t *encoded_len);
32-
33-
jsurl::SpecString encode(JSContext *cx, JS::HandleValue val);
34-
3525
enum JSBuiltinErrNum {
3626
#define MSG_DEF(name, count, exception, format) name,
3727
#include "./builtin-error-numbers.msg"

runtime/js-compute-runtime/builtins/backend.cpp

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "builtins/backend.h"
2121
#include "builtins/request-response.h"
22+
#include "core/encode.h"
2223
#include "host_interface/fastly.h"
2324
#include "js-compute-builtins.h"
2425
#include "js/Conversions.h"
@@ -713,23 +714,20 @@ JS::Result<mozilla::Ok> Backend::register_dynamic_backend(JSContext *cx, JS::Han
713714
MOZ_ASSERT(is_instance(backend));
714715

715716
JS::RootedString name(cx, JS::GetReservedSlot(backend, Backend::Slots::Name).toString());
716-
size_t name_len;
717-
JS::UniqueChars nameChars = encode(cx, name, &name_len);
718-
std::string_view name_str{nameChars.get(), name_len};
717+
auto nameChars = fastly::core::encode(cx, name);
718+
std::string_view name_str = nameChars;
719719

720720
JS::RootedString target(cx, JS::GetReservedSlot(backend, Backend::Slots::Target).toString());
721-
size_t target_len;
722-
JS::UniqueChars targetChars = encode(cx, target, &target_len);
723-
std::string_view target_str{targetChars.get(), target_len};
721+
auto targetChars = fastly::core::encode(cx, target);
722+
std::string_view target_str = targetChars;
724723

725724
BackendConfig backend_config;
726725

727726
auto hostOverrideSlot = JS::GetReservedSlot(backend, Backend::Slots::HostOverride);
728727
if (!hostOverrideSlot.isNullOrUndefined()) {
729728
JS::RootedString hostOverrideString(cx, hostOverrideSlot.toString());
730-
size_t hostOverride_len;
731-
JS::UniqueChars hostOverrideChars = encode(cx, hostOverrideString, &hostOverride_len);
732-
backend_config.host_override.emplace(std::move(hostOverrideChars), hostOverride_len);
729+
auto hostOverrideChars = fastly::core::encode(cx, hostOverrideString);
730+
backend_config.host_override.emplace(std::move(hostOverrideChars));
733731
}
734732

735733
auto connectTimeoutSlot = JS::GetReservedSlot(backend, Backend::Slots::ConnectTimeout);
@@ -770,35 +768,29 @@ JS::Result<mozilla::Ok> Backend::register_dynamic_backend(JSContext *cx, JS::Han
770768
auto certificateHostnameSlot = JS::GetReservedSlot(backend, Backend::Slots::CertificateHostname);
771769
if (!certificateHostnameSlot.isNullOrUndefined()) {
772770
JS::RootedString certificateHostnameString(cx, certificateHostnameSlot.toString());
773-
size_t certificateHostname_len;
774-
JS::UniqueChars certificateHostnameChars =
775-
encode(cx, certificateHostnameString, &certificateHostname_len);
776-
backend_config.cert_hostname.emplace(std::move(certificateHostnameChars),
777-
certificateHostname_len);
771+
auto certificateHostnameChars = fastly::core::encode(cx, certificateHostnameString);
772+
backend_config.cert_hostname.emplace(std::move(certificateHostnameChars));
778773
}
779774

780775
auto caCertificateSlot = JS::GetReservedSlot(backend, Backend::Slots::CaCertificate);
781776
if (!caCertificateSlot.isNullOrUndefined()) {
782777
JS::RootedString caCertificateString(cx, caCertificateSlot.toString());
783-
size_t caCertificate_len;
784-
JS::UniqueChars caCertificateChars = encode(cx, caCertificateString, &caCertificate_len);
785-
backend_config.ca_cert.emplace(std::move(caCertificateChars), caCertificate_len);
778+
auto caCertificateChars = fastly::core::encode(cx, caCertificateString);
779+
backend_config.ca_cert.emplace(std::move(caCertificateChars));
786780
}
787781

788782
auto ciphersSlot = JS::GetReservedSlot(backend, Backend::Slots::Ciphers);
789783
if (!ciphersSlot.isNullOrUndefined()) {
790784
JS::RootedString ciphersString(cx, ciphersSlot.toString());
791-
size_t ciphers_len;
792-
JS::UniqueChars ciphersChars = encode(cx, ciphersString, &ciphers_len);
793-
backend_config.ciphers.emplace(std::move(ciphersChars), ciphers_len);
785+
auto ciphersChars = fastly::core::encode(cx, ciphersString);
786+
backend_config.ciphers.emplace(std::move(ciphersChars));
794787
}
795788

796789
auto sniHostnameSlot = JS::GetReservedSlot(backend, Backend::Slots::SniHostname);
797790
if (!sniHostnameSlot.isNullOrUndefined()) {
798791
JS::RootedString sniHostnameString(cx, sniHostnameSlot.toString());
799-
size_t sniHostname_len;
800-
JS::UniqueChars sniHostnameChars = encode(cx, sniHostnameString, &sniHostname_len);
801-
backend_config.sni_hostname.emplace(std::move(sniHostnameChars), sniHostname_len);
792+
auto sniHostnameChars = fastly::core::encode(cx, sniHostnameString);
793+
backend_config.sni_hostname.emplace(std::move(sniHostnameChars));
802794
}
803795

804796
auto res = HttpReq::register_dynamic_backend(name_str, target_str, backend_config);
@@ -915,7 +907,7 @@ bool Backend::set_target(JSContext *cx, JSObject *backend, JS::HandleValue targe
915907
return false;
916908
}
917909

918-
auto targetStringSlice = encode(cx, target_val);
910+
auto targetStringSlice = fastly::core::encode_spec_string(cx, target_val);
919911
if (!targetStringSlice.data) {
920912
return false;
921913
}
@@ -948,7 +940,7 @@ bool Backend::set_target(JSContext *cx, JSObject *backend, JS::HandleValue targe
948940

949941
JSObject *Backend::create(JSContext *cx, JS::HandleObject request) {
950942
JS::RootedValue request_url(cx, RequestOrResponse::url(request));
951-
auto url_string = encode(cx, request_url);
943+
auto url_string = fastly::core::encode_spec_string(cx, request_url);
952944
if (!url_string.data) {
953945
return nullptr;
954946
}
@@ -1132,7 +1124,7 @@ bool Backend::constructor(JSContext *cx, unsigned argc, JS::Value *vp) {
11321124
return false;
11331125
}
11341126

1135-
if (isnan(version)) {
1127+
if (std::isnan(version)) {
11361128
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BACKEND_TLS_MIN_INVALID);
11371129
return false;
11381130
}
@@ -1272,27 +1264,28 @@ bool Backend::constructor(JSContext *cx, unsigned argc, JS::Value *vp) {
12721264
if (!JS_GetProperty(cx, configuration, "ciphers", &ciphers_val)) {
12731265
return false;
12741266
}
1275-
size_t length;
1276-
auto ciphers_chars = encode(cx, ciphers_val, &length);
1267+
auto ciphers_chars = fastly::core::encode(cx, ciphers_val);
12771268
if (!ciphers_chars) {
12781269
return false;
12791270
}
1280-
if (length == 0) {
1271+
if (ciphers_chars.size() == 0) {
12811272
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BACKEND_CIPHERS_EMPTY);
12821273
return false;
12831274
}
1284-
std::string cipherSpec(ciphers_chars.get(), length);
1275+
std::string cipherSpec(ciphers_chars.begin(), ciphers_chars.len);
12851276
if (!isCipherSuiteSupportedByFastly(cipherSpec)) {
12861277
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BACKEND_CIPHERS_NOT_AVALIABLE);
12871278
return false;
12881279
}
1289-
JS::SetReservedSlot(backend, Backend::Slots::Ciphers,
1290-
JS::StringValue(JS_NewStringCopyN(cx, ciphers_chars.get(), length)));
1280+
JS::SetReservedSlot(
1281+
backend, Backend::Slots::Ciphers,
1282+
JS::StringValue(JS_NewStringCopyN(cx, ciphers_chars.begin(), ciphers_chars.len)));
12911283
auto ciphersSlot = JS::GetReservedSlot(backend, Backend::Slots::Ciphers);
12921284
if (!ciphersSlot.isNullOrUndefined()) {
12931285
JS::RootedString ciphers(cx, ciphersSlot.toString());
1294-
size_t ciphers_len;
1295-
JS::UniqueChars ciphersChars = encode(cx, ciphers, &ciphers_len);
1286+
1287+
// TODO: what should this be used for?
1288+
auto ciphersChars = fastly::core::encode(cx, ciphers);
12961289
}
12971290
}
12981291

runtime/js-compute-runtime/builtins/cache-override.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "js/Conversions.h"
99

1010
#include "cache-override.h"
11+
#include "core/encode.h"
1112
#include "host_interface/fastly.h"
1213
#include "host_interface/host_api.h"
1314
#include "js-compute-builtins.h"
@@ -141,21 +142,22 @@ bool CacheOverride::mode_set(JSContext *cx, JS::HandleObject self, JS::HandleVal
141142
"CacheOverride");
142143
return false;
143144
}
144-
size_t mode_len;
145-
JS::UniqueChars mode_chars = encode(cx, val, &mode_len);
146-
if (!mode_chars)
145+
146+
auto mode_chars = fastly::core::encode(cx, val);
147+
if (!mode_chars) {
147148
return false;
149+
}
148150

149151
CacheOverride::CacheOverrideMode mode;
150-
if (!strcmp(mode_chars.get(), "none")) {
152+
if (!strcmp(mode_chars.begin(), "none")) {
151153
mode = CacheOverride::CacheOverrideMode::None;
152-
} else if (!strcmp(mode_chars.get(), "pass")) {
154+
} else if (!strcmp(mode_chars.begin(), "pass")) {
153155
mode = CacheOverride::CacheOverrideMode::Pass;
154-
} else if (!strcmp(mode_chars.get(), "override")) {
156+
} else if (!strcmp(mode_chars.begin(), "override")) {
155157
mode = CacheOverride::CacheOverrideMode::Override;
156158
} else {
157159
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CACHE_OVERRIDE_MODE_INVALID,
158-
mode_chars.get());
160+
mode_chars.begin());
159161
return false;
160162
}
161163

runtime/js-compute-runtime/builtins/cache-simple.cpp

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "builtin.h"
33
#include "builtins/native-stream-source.h"
44
#include "builtins/shared/url.h"
5+
#include "core/encode.h"
56
#include "host_interface/fastly.h"
67
#include "host_interface/host_api.h"
78
#include "js-compute-builtins.h"
@@ -124,10 +125,12 @@ JS::Result<std::tuple<JS::UniqueChars, size_t>> convertBodyInit(JSContext *cx,
124125
length = slice.len;
125126
} else {
126127
// Convert into a String following https://tc39.es/ecma262/#sec-tostring
127-
buf = encode(cx, bodyInit, &length);
128-
if (!buf) {
128+
auto str = fastly::core::encode(cx, bodyInit);
129+
if (!str) {
129130
return JS::Result<std::tuple<JS::UniqueChars, size_t>>(JS::Error());
130131
}
132+
buf.reset(str.ptr.release());
133+
length = str.len;
131134
}
132135
return JS::Result<std::tuple<JS::UniqueChars, size_t>>(std::make_tuple(std::move(buf), length));
133136
}
@@ -393,16 +396,15 @@ bool SimpleCache::getOrSetThenHandler(JSContext *cx, JS::HandleObject owner, JS:
393396
// We create a surrogate-key from the cache-key, as this allows the cached contents to be purgable
394397
// from within the JavaScript application
395398
// This is because the cache API currently only supports purging via surrogate-key
396-
fastly_world_string_t key;
397-
JS::UniqueChars key_chars = encode(cx, keyVal, &key.len);
399+
auto key_chars = fastly::core::encode(cx, keyVal);
398400
if (!key_chars) {
399401
if (!fastly_compute_at_edge_fastly_transaction_cancel(handle, &err)) {
400402
HANDLE_ERROR(cx, err);
401403
return RejectPromiseWithPendingError(cx, promise);
402404
}
403405
return RejectPromiseWithPendingError(cx, promise);
404406
}
405-
key.ptr = key_chars.get();
407+
fastly_world_string_t key{.ptr = key_chars.begin(), .len = key_chars.len};
406408
auto key_result = createSurrogateKeysFromCacheKey(cx, key);
407409
if (key_result.isErr()) {
408410
if (!fastly_compute_at_edge_fastly_transaction_cancel(handle, &err)) {
@@ -502,13 +504,12 @@ bool SimpleCache::getOrSet(JSContext *cx, unsigned argc, JS::Value *vp) {
502504
return false;
503505
}
504506

505-
fastly_world_string_t key;
506507
// Convert key parameter into a string and check the value adheres to our validation rules.
507-
JS::UniqueChars key_chars = encode(cx, args.get(0), &key.len);
508+
auto key_chars = fastly::core::encode(cx, args.get(0));
508509
if (!key_chars) {
509510
return false;
510511
}
511-
key.ptr = key_chars.get();
512+
fastly_world_string_t key{.ptr = key_chars.begin(), .len = key_chars.len};
512513

513514
if (key.len == 0) {
514515
JS_ReportErrorASCII(cx, "SimpleCache.getOrSet: key can not be an empty string");
@@ -663,14 +664,13 @@ bool SimpleCache::set(JSContext *cx, unsigned argc, JS::Value *vp) {
663664
return false;
664665
}
665666

666-
fastly_world_string_t key;
667667
// Convert key parameter into a string and check the value adheres to our validation rules.
668-
JS::UniqueChars key_chars = encode(cx, args.get(0), &key.len);
668+
auto key_chars = fastly::core::encode(cx, args.get(0));
669669
if (!key_chars) {
670670
return false;
671671
}
672-
key.ptr = key_chars.get();
673672

673+
fastly_world_string_t key{.ptr = key_chars.begin(), .len = key_chars.len};
674674
if (key.len == 0) {
675675
JS_ReportErrorASCII(cx, "SimpleCache.set: key can not be an empty string");
676676
return false;
@@ -809,14 +809,13 @@ bool SimpleCache::get(JSContext *cx, unsigned argc, JS::Value *vp) {
809809
return false;
810810
}
811811

812-
fastly_world_string_t key;
813812
// Convert key parameter into a string and check the value adheres to our validation rules.
814-
JS::UniqueChars key_chars = encode(cx, args[0], &key.len);
813+
auto key_chars = fastly::core::encode(cx, args[0]);
815814
if (!key_chars) {
816815
return false;
817816
}
818-
key.ptr = key_chars.get();
819817

818+
fastly_world_string_t key{.ptr = key_chars.begin(), .len = key_chars.len};
820819
if (key.len == 0) {
821820
JS_ReportErrorASCII(cx, "SimpleCache.get: key can not be an empty string");
822821
return false;
@@ -865,14 +864,13 @@ bool SimpleCache::purge(JSContext *cx, unsigned argc, JS::Value *vp) {
865864
return false;
866865
}
867866

868-
fastly_world_string_t key;
869867
// Convert key parameter into a string and check the value adheres to our validation rules.
870-
JS::UniqueChars key_chars = encode(cx, args.get(0), &key.len);
868+
auto key_chars = fastly::core::encode(cx, args.get(0));
871869
if (!key_chars) {
872870
return false;
873871
}
874-
key.ptr = key_chars.get();
875872

873+
fastly_world_string_t key{.ptr = key_chars.begin(), .len = key_chars.len};
876874
if (key.len == 0) {
877875
JS_ReportErrorASCII(cx, "SimpleCache.purge: key can not be an empty string");
878876
return false;
@@ -894,12 +892,12 @@ bool SimpleCache::purge(JSContext *cx, unsigned argc, JS::Value *vp) {
894892
if (!JS_GetProperty(cx, options, "scope", &scope_val)) {
895893
return false;
896894
}
897-
size_t length;
898-
auto scope_chars = encode(cx, scope_val, &length);
895+
auto scope_chars = fastly::core::encode(cx, scope_val);
899896
if (!scope_chars) {
900897
return false;
901898
}
902-
std::string_view scope(scope_chars.get(), length);
899+
900+
std::string_view scope = scope_chars;
903901
std::string surrogate_key;
904902
if (scope == "pop") {
905903
auto surrogate_key_result = createPopSurrogateKeyFromCacheKey(cx, key);

runtime/js-compute-runtime/builtins/compression-stream.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "builtins/compression-stream.h"
1212
#include "builtins/transform-stream-default-controller.h"
1313
#include "builtins/transform-stream.h"
14+
#include "core/encode.h"
1415
#include "js-compute-builtins.h"
1516

1617
namespace builtins {
@@ -291,23 +292,23 @@ bool CompressionStream::constructor(JSContext *cx, unsigned argc, JS::Value *vp)
291292
// `TypeError`.
292293
CTOR_HEADER("CompressionStream", 1);
293294

294-
size_t format_len;
295-
JS::UniqueChars format_chars = encode(cx, args[0], &format_len);
296-
if (!format_chars)
295+
auto format_chars = fastly::core::encode(cx, args[0]);
296+
if (!format_chars) {
297297
return false;
298+
}
298299

299300
enum Format format;
300-
if (!strcmp(format_chars.get(), "deflate-raw")) {
301+
if (!strcmp(format_chars.begin(), "deflate-raw")) {
301302
format = Format::DeflateRaw;
302-
} else if (!strcmp(format_chars.get(), "deflate")) {
303+
} else if (!strcmp(format_chars.begin(), "deflate")) {
303304
format = Format::Deflate;
304-
} else if (!strcmp(format_chars.get(), "gzip")) {
305+
} else if (!strcmp(format_chars.begin(), "gzip")) {
305306
format = Format::GZIP;
306307
} else {
307308
JS_ReportErrorUTF8(cx,
308309
"'format' has to be \"deflate\" or \"gzip\", "
309310
"but got \"%s\"",
310-
format_chars.get());
311+
format_chars.begin());
311312
return false;
312313
}
313314

0 commit comments

Comments
 (0)