2024-01-01 14:58:21 -05:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2020-08-07 10:55:02 -04:00
|
|
|
|
2021-07-05 09:34:37 -04:00
|
|
|
mod blob;
|
2022-01-24 12:03:06 -05:00
|
|
|
mod compression;
|
2023-05-17 15:59:55 -04:00
|
|
|
mod hr_timer_lock;
|
2021-06-21 13:53:52 -04:00
|
|
|
mod message_port;
|
feat(ext/web): resourceForReadableStream (#20180)
Extracted from fast streams work.
This is a resource wrapper for `ReadableStream`, allowing us to treat
all `ReadableStream` instances as resources, and remove special paths in
both `fetch` and `serve`.
Performance with a ReadableStream response yields ~18% improvement:
```
return new Response(new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]));
controller.close();
}
})
```
This patch:
```
12:36 $ third_party/prebuilt/mac/wrk http://localhost:8080
Running 10s test @ http://localhost:8080
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 99.96us 100.03us 6.65ms 98.84%
Req/Sec 47.73k 2.43k 51.02k 89.11%
959308 requests in 10.10s, 117.10MB read
Requests/sec: 94978.71
Transfer/sec: 11.59MB
```
main:
```
Running 10s test @ http://localhost:8080
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 163.03us 685.51us 19.73ms 99.27%
Req/Sec 39.50k 3.98k 66.11k 95.52%
789582 requests in 10.10s, 82.83MB read
Requests/sec: 78182.65
Transfer/sec: 8.20MB
```
2023-08-17 09:52:37 -04:00
|
|
|
mod stream_resource;
|
2022-02-15 06:17:30 -05:00
|
|
|
mod timers;
|
2021-06-21 13:53:52 -04:00
|
|
|
|
2021-06-05 17:10:07 -04:00
|
|
|
use deno_core::error::range_error;
|
|
|
|
use deno_core::error::type_error;
|
2021-06-05 13:30:20 -04:00
|
|
|
use deno_core::error::AnyError;
|
2023-10-05 08:34:38 -04:00
|
|
|
use deno_core::op2;
|
2021-06-10 09:26:10 -04:00
|
|
|
use deno_core::url::Url;
|
2022-09-17 07:18:15 -04:00
|
|
|
use deno_core::v8;
|
2022-03-05 14:12:30 -05:00
|
|
|
use deno_core::ByteString;
|
2021-06-05 17:10:07 -04:00
|
|
|
use deno_core::OpState;
|
|
|
|
use deno_core::Resource;
|
|
|
|
use deno_core::ResourceId;
|
2023-06-22 17:37:56 -04:00
|
|
|
use deno_core::ToJsBuffer;
|
2022-03-15 19:22:00 -04:00
|
|
|
use deno_core::U16String;
|
2022-09-17 07:18:15 -04:00
|
|
|
|
2021-06-05 17:10:07 -04:00
|
|
|
use encoding_rs::CoderResult;
|
|
|
|
use encoding_rs::Decoder;
|
|
|
|
use encoding_rs::DecoderResult;
|
|
|
|
use encoding_rs::Encoding;
|
|
|
|
use std::borrow::Cow;
|
|
|
|
use std::cell::RefCell;
|
2021-06-05 13:30:20 -04:00
|
|
|
use std::fmt;
|
2022-03-16 20:25:44 -04:00
|
|
|
use std::path::PathBuf;
|
2023-07-01 18:52:30 -04:00
|
|
|
use std::sync::Arc;
|
2021-06-05 17:10:07 -04:00
|
|
|
use std::usize;
|
2020-08-07 10:55:02 -04:00
|
|
|
|
2021-07-05 09:34:37 -04:00
|
|
|
use crate::blob::op_blob_create_object_url;
|
|
|
|
use crate::blob::op_blob_create_part;
|
fix: a `Request` whose URL is a revoked blob URL should still fetch (#11947)
In the spec, a URL record has an associated "blob URL entry", which for
`blob:` URLs is populated during parsing to contain a reference to the
`Blob` object that backs that object URL. It is this blob URL entry that
the `fetch` API uses to resolve an object URL.
Therefore, since the `Request` constructor parses URL inputs, it will
have an associated blob URL entry which will be used when fetching, even
if the object URL has been revoked since the construction of the
`Request` object. (The `Request` constructor takes the URL as a string
and parses it, so the object URL must be live at the time it is called.)
This PR adds a new `blobFromObjectUrl` JS function (backed by a new
`op_blob_from_object_url` op) that, if the URL is a valid object URL,
returns a new `Blob` object whose parts are references to the same Rust
`BlobPart`s used by the original `Blob` object. It uses this function to
add a new `blobUrlEntry` field to inner requests, which will be `null`
or such a `Blob`, and then uses `Blob.prototype.stream()` as the
response's body. As a result of this, the `blob:` URL resolution from
`op_fetch` is now useless, and has been removed.
2021-09-08 05:29:21 -04:00
|
|
|
use crate::blob::op_blob_from_object_url;
|
2021-07-05 09:34:37 -04:00
|
|
|
use crate::blob::op_blob_read_part;
|
|
|
|
use crate::blob::op_blob_remove_part;
|
|
|
|
use crate::blob::op_blob_revoke_object_url;
|
|
|
|
use crate::blob::op_blob_slice_part;
|
|
|
|
pub use crate::blob::Blob;
|
|
|
|
pub use crate::blob::BlobPart;
|
|
|
|
pub use crate::blob::BlobStore;
|
|
|
|
pub use crate::blob::InMemoryBlobPart;
|
|
|
|
|
|
|
|
pub use crate::message_port::create_entangled_message_port;
|
2021-06-21 13:53:52 -04:00
|
|
|
use crate::message_port::op_message_port_create_entangled;
|
|
|
|
use crate::message_port::op_message_port_post_message;
|
|
|
|
use crate::message_port::op_message_port_recv_message;
|
2021-07-05 09:34:37 -04:00
|
|
|
pub use crate::message_port::JsMessageData;
|
|
|
|
pub use crate::message_port::MessagePort;
|
2021-06-21 13:53:52 -04:00
|
|
|
|
2022-02-15 06:17:30 -05:00
|
|
|
use crate::timers::op_now;
|
|
|
|
use crate::timers::op_sleep;
|
|
|
|
use crate::timers::op_timer_handle;
|
|
|
|
use crate::timers::StartTime;
|
|
|
|
pub use crate::timers::TimersPermission;
|
|
|
|
|
2023-03-17 14:22:15 -04:00
|
|
|
deno_core::extension!(deno_web,
|
|
|
|
deps = [ deno_webidl, deno_console, deno_url ],
|
|
|
|
parameters = [P: TimersPermission],
|
|
|
|
ops = [
|
|
|
|
op_base64_decode,
|
|
|
|
op_base64_encode,
|
|
|
|
op_base64_atob,
|
|
|
|
op_base64_btoa,
|
|
|
|
op_encoding_normalize_label,
|
|
|
|
op_encoding_decode_single,
|
|
|
|
op_encoding_decode_utf8,
|
|
|
|
op_encoding_new_decoder,
|
|
|
|
op_encoding_decode,
|
|
|
|
op_encoding_encode_into,
|
|
|
|
op_blob_create_part,
|
|
|
|
op_blob_slice_part,
|
|
|
|
op_blob_read_part,
|
|
|
|
op_blob_remove_part,
|
|
|
|
op_blob_create_object_url,
|
|
|
|
op_blob_revoke_object_url,
|
|
|
|
op_blob_from_object_url,
|
|
|
|
op_message_port_create_entangled,
|
|
|
|
op_message_port_post_message,
|
|
|
|
op_message_port_recv_message,
|
|
|
|
compression::op_compression_new,
|
|
|
|
compression::op_compression_write,
|
|
|
|
compression::op_compression_finish,
|
|
|
|
op_now<P>,
|
|
|
|
op_timer_handle,
|
|
|
|
op_sleep,
|
|
|
|
op_transfer_arraybuffer,
|
feat(ext/web): resourceForReadableStream (#20180)
Extracted from fast streams work.
This is a resource wrapper for `ReadableStream`, allowing us to treat
all `ReadableStream` instances as resources, and remove special paths in
both `fetch` and `serve`.
Performance with a ReadableStream response yields ~18% improvement:
```
return new Response(new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]));
controller.close();
}
})
```
This patch:
```
12:36 $ third_party/prebuilt/mac/wrk http://localhost:8080
Running 10s test @ http://localhost:8080
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 99.96us 100.03us 6.65ms 98.84%
Req/Sec 47.73k 2.43k 51.02k 89.11%
959308 requests in 10.10s, 117.10MB read
Requests/sec: 94978.71
Transfer/sec: 11.59MB
```
main:
```
Running 10s test @ http://localhost:8080
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 163.03us 685.51us 19.73ms 99.27%
Req/Sec 39.50k 3.98k 66.11k 95.52%
789582 requests in 10.10s, 82.83MB read
Requests/sec: 78182.65
Transfer/sec: 8.20MB
```
2023-08-17 09:52:37 -04:00
|
|
|
stream_resource::op_readable_stream_resource_allocate,
|
2023-12-01 10:56:10 -05:00
|
|
|
stream_resource::op_readable_stream_resource_allocate_sized,
|
feat(ext/web): resourceForReadableStream (#20180)
Extracted from fast streams work.
This is a resource wrapper for `ReadableStream`, allowing us to treat
all `ReadableStream` instances as resources, and remove special paths in
both `fetch` and `serve`.
Performance with a ReadableStream response yields ~18% improvement:
```
return new Response(new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]));
controller.close();
}
})
```
This patch:
```
12:36 $ third_party/prebuilt/mac/wrk http://localhost:8080
Running 10s test @ http://localhost:8080
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 99.96us 100.03us 6.65ms 98.84%
Req/Sec 47.73k 2.43k 51.02k 89.11%
959308 requests in 10.10s, 117.10MB read
Requests/sec: 94978.71
Transfer/sec: 11.59MB
```
main:
```
Running 10s test @ http://localhost:8080
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 163.03us 685.51us 19.73ms 99.27%
Req/Sec 39.50k 3.98k 66.11k 95.52%
789582 requests in 10.10s, 82.83MB read
Requests/sec: 78182.65
Transfer/sec: 8.20MB
```
2023-08-17 09:52:37 -04:00
|
|
|
stream_resource::op_readable_stream_resource_get_sink,
|
|
|
|
stream_resource::op_readable_stream_resource_write_error,
|
|
|
|
stream_resource::op_readable_stream_resource_write_buf,
|
2023-09-23 10:55:28 -04:00
|
|
|
stream_resource::op_readable_stream_resource_write_sync,
|
feat(ext/web): resourceForReadableStream (#20180)
Extracted from fast streams work.
This is a resource wrapper for `ReadableStream`, allowing us to treat
all `ReadableStream` instances as resources, and remove special paths in
both `fetch` and `serve`.
Performance with a ReadableStream response yields ~18% improvement:
```
return new Response(new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]));
controller.close();
}
})
```
This patch:
```
12:36 $ third_party/prebuilt/mac/wrk http://localhost:8080
Running 10s test @ http://localhost:8080
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 99.96us 100.03us 6.65ms 98.84%
Req/Sec 47.73k 2.43k 51.02k 89.11%
959308 requests in 10.10s, 117.10MB read
Requests/sec: 94978.71
Transfer/sec: 11.59MB
```
main:
```
Running 10s test @ http://localhost:8080
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 163.03us 685.51us 19.73ms 99.27%
Req/Sec 39.50k 3.98k 66.11k 95.52%
789582 requests in 10.10s, 82.83MB read
Requests/sec: 78182.65
Transfer/sec: 8.20MB
```
2023-08-17 09:52:37 -04:00
|
|
|
stream_resource::op_readable_stream_resource_close,
|
|
|
|
stream_resource::op_readable_stream_resource_await_close,
|
2023-03-17 14:22:15 -04:00
|
|
|
],
|
|
|
|
esm = [
|
|
|
|
"00_infra.js",
|
|
|
|
"01_dom_exception.js",
|
|
|
|
"01_mimesniff.js",
|
|
|
|
"02_event.js",
|
|
|
|
"02_structured_clone.js",
|
|
|
|
"02_timers.js",
|
|
|
|
"03_abort_signal.js",
|
|
|
|
"04_global_interfaces.js",
|
|
|
|
"05_base64.js",
|
|
|
|
"06_streams.js",
|
|
|
|
"08_text_encoding.js",
|
|
|
|
"09_file.js",
|
|
|
|
"10_filereader.js",
|
|
|
|
"12_location.js",
|
|
|
|
"13_message_port.js",
|
|
|
|
"14_compression.js",
|
|
|
|
"15_performance.js",
|
2024-02-06 19:11:15 -05:00
|
|
|
"16_image_data.js",
|
2023-03-17 14:22:15 -04:00
|
|
|
],
|
2023-03-17 18:15:27 -04:00
|
|
|
options = {
|
2023-07-01 18:52:30 -04:00
|
|
|
blob_store: Arc<BlobStore>,
|
2023-03-17 14:22:15 -04:00
|
|
|
maybe_location: Option<Url>,
|
|
|
|
},
|
2023-03-17 18:15:27 -04:00
|
|
|
state = |state, options| {
|
|
|
|
state.put(options.blob_store);
|
|
|
|
if let Some(location) = options.maybe_location {
|
2023-03-17 14:22:15 -04:00
|
|
|
state.put(Location(location));
|
|
|
|
}
|
|
|
|
state.put(StartTime::now());
|
|
|
|
}
|
|
|
|
);
|
2020-08-07 10:55:02 -04:00
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
|
|
|
#[serde]
|
|
|
|
fn op_base64_decode(#[string] input: String) -> Result<ToJsBuffer, AnyError> {
|
2022-06-30 04:48:06 -04:00
|
|
|
let mut s = input.into_bytes();
|
2023-01-18 09:35:24 -05:00
|
|
|
let decoded_len = forgiving_base64_decode_inplace(&mut s)?;
|
2022-06-30 04:48:06 -04:00
|
|
|
s.truncate(decoded_len);
|
|
|
|
Ok(s.into())
|
2022-03-05 14:12:30 -05:00
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
|
|
|
#[serde]
|
|
|
|
fn op_base64_atob(#[serde] mut s: ByteString) -> Result<ByteString, AnyError> {
|
2023-01-18 09:35:24 -05:00
|
|
|
let decoded_len = forgiving_base64_decode_inplace(&mut s)?;
|
2022-06-30 04:48:06 -04:00
|
|
|
s.truncate(decoded_len);
|
|
|
|
Ok(s)
|
2022-06-29 11:42:39 -04:00
|
|
|
}
|
2022-03-05 14:12:30 -05:00
|
|
|
|
2022-06-29 11:42:39 -04:00
|
|
|
/// See <https://infra.spec.whatwg.org/#forgiving-base64>
|
2022-06-30 04:48:06 -04:00
|
|
|
#[inline]
|
2023-01-18 09:35:24 -05:00
|
|
|
fn forgiving_base64_decode_inplace(
|
|
|
|
input: &mut [u8],
|
|
|
|
) -> Result<usize, AnyError> {
|
2023-06-05 20:35:39 -04:00
|
|
|
let error =
|
2022-06-29 11:42:39 -04:00
|
|
|
|| DomExceptionInvalidCharacterError::new("Failed to decode base64");
|
2023-01-18 09:35:24 -05:00
|
|
|
let decoded =
|
|
|
|
base64_simd::forgiving_decode_inplace(input).map_err(|_| error())?;
|
2022-06-30 04:48:06 -04:00
|
|
|
Ok(decoded.len())
|
2021-06-05 17:10:07 -04:00
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
|
|
|
#[string]
|
|
|
|
fn op_base64_encode(#[buffer] s: &[u8]) -> String {
|
2022-09-07 06:51:47 -04:00
|
|
|
forgiving_base64_encode(s)
|
2022-03-05 14:12:30 -05:00
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
|
|
|
#[string]
|
|
|
|
fn op_base64_btoa(#[serde] s: ByteString) -> String {
|
2022-06-29 11:42:39 -04:00
|
|
|
forgiving_base64_encode(s.as_ref())
|
2022-03-05 14:12:30 -05:00
|
|
|
}
|
|
|
|
|
2022-06-29 11:42:39 -04:00
|
|
|
/// See <https://infra.spec.whatwg.org/#forgiving-base64>
|
2022-06-30 04:48:06 -04:00
|
|
|
#[inline]
|
2022-06-29 11:42:39 -04:00
|
|
|
fn forgiving_base64_encode(s: &[u8]) -> String {
|
2023-01-18 09:35:24 -05:00
|
|
|
base64_simd::STANDARD.encode_to_string(s)
|
2021-06-05 17:10:07 -04:00
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
|
|
|
#[string]
|
|
|
|
fn op_encoding_normalize_label(
|
|
|
|
#[string] label: String,
|
|
|
|
) -> Result<String, AnyError> {
|
2021-06-05 17:10:07 -04:00
|
|
|
let encoding = Encoding::for_label_no_replacement(label.as_bytes())
|
|
|
|
.ok_or_else(|| {
|
|
|
|
range_error(format!(
|
2023-01-27 10:43:16 -05:00
|
|
|
"The encoding label provided ('{label}') is invalid."
|
2021-06-05 17:10:07 -04:00
|
|
|
))
|
|
|
|
})?;
|
|
|
|
Ok(encoding.name().to_lowercase())
|
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
2022-11-11 09:37:18 -05:00
|
|
|
fn op_encoding_decode_utf8<'a>(
|
|
|
|
scope: &mut v8::HandleScope<'a>,
|
2023-10-05 08:34:38 -04:00
|
|
|
#[anybuffer] zero_copy: &[u8],
|
2022-11-11 09:37:18 -05:00
|
|
|
ignore_bom: bool,
|
2023-10-05 08:34:38 -04:00
|
|
|
) -> Result<v8::Local<'a, v8::String>, AnyError> {
|
2022-11-11 09:37:18 -05:00
|
|
|
let buf = &zero_copy;
|
|
|
|
|
|
|
|
let buf = if !ignore_bom
|
|
|
|
&& buf.len() >= 3
|
|
|
|
&& buf[0] == 0xef
|
|
|
|
&& buf[1] == 0xbb
|
|
|
|
&& buf[2] == 0xbf
|
|
|
|
{
|
|
|
|
&buf[3..]
|
|
|
|
} else {
|
|
|
|
buf
|
|
|
|
};
|
|
|
|
|
|
|
|
// If `String::new_from_utf8()` returns `None`, this means that the
|
|
|
|
// length of the decoded string would be longer than what V8 can
|
|
|
|
// handle. In this case we return `RangeError`.
|
|
|
|
//
|
|
|
|
// For more details see:
|
|
|
|
// - https://encoding.spec.whatwg.org/#dom-textdecoder-decode
|
|
|
|
// - https://github.com/denoland/deno/issues/6649
|
|
|
|
// - https://github.com/v8/v8/blob/d68fb4733e39525f9ff0a9222107c02c28096e2a/include/v8.h#L3277-L3278
|
|
|
|
match v8::String::new_from_utf8(scope, buf, v8::NewStringType::Normal) {
|
2023-10-05 08:34:38 -04:00
|
|
|
Some(text) => Ok(text),
|
2022-11-11 09:37:18 -05:00
|
|
|
None => Err(type_error("buffer exceeds maximum length")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
|
|
|
#[serde]
|
2022-05-17 09:52:48 -04:00
|
|
|
fn op_encoding_decode_single(
|
2023-10-05 08:34:38 -04:00
|
|
|
#[anybuffer] data: &[u8],
|
|
|
|
#[string] label: String,
|
2022-09-01 06:51:13 -04:00
|
|
|
fatal: bool,
|
|
|
|
ignore_bom: bool,
|
2022-05-17 09:52:48 -04:00
|
|
|
) -> Result<U16String, AnyError> {
|
|
|
|
let encoding = Encoding::for_label(label.as_bytes()).ok_or_else(|| {
|
|
|
|
range_error(format!(
|
2023-01-27 10:43:16 -05:00
|
|
|
"The encoding label provided ('{label}') is invalid."
|
2022-05-17 09:52:48 -04:00
|
|
|
))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let mut decoder = if ignore_bom {
|
|
|
|
encoding.new_decoder_without_bom_handling()
|
|
|
|
} else {
|
|
|
|
encoding.new_decoder_with_bom_removal()
|
|
|
|
};
|
|
|
|
|
|
|
|
let max_buffer_length = decoder
|
|
|
|
.max_utf16_buffer_length(data.len())
|
|
|
|
.ok_or_else(|| range_error("Value too large to decode."))?;
|
|
|
|
|
|
|
|
let mut output = vec![0; max_buffer_length];
|
|
|
|
|
|
|
|
if fatal {
|
|
|
|
let (result, _, written) =
|
2022-09-07 06:51:47 -04:00
|
|
|
decoder.decode_to_utf16_without_replacement(data, &mut output, true);
|
2022-05-17 09:52:48 -04:00
|
|
|
match result {
|
|
|
|
DecoderResult::InputEmpty => {
|
|
|
|
output.truncate(written);
|
|
|
|
Ok(output.into())
|
|
|
|
}
|
|
|
|
DecoderResult::OutputFull => {
|
|
|
|
Err(range_error("Provided buffer too small."))
|
|
|
|
}
|
|
|
|
DecoderResult::Malformed(_, _) => {
|
|
|
|
Err(type_error("The encoded data is not valid."))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let (result, _, written, _) =
|
2022-09-07 06:51:47 -04:00
|
|
|
decoder.decode_to_utf16(data, &mut output, true);
|
2022-05-17 09:52:48 -04:00
|
|
|
match result {
|
|
|
|
CoderResult::InputEmpty => {
|
|
|
|
output.truncate(written);
|
|
|
|
Ok(output.into())
|
|
|
|
}
|
|
|
|
CoderResult::OutputFull => Err(range_error("Provided buffer too small.")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2(fast)]
|
|
|
|
#[smi]
|
2021-06-05 17:10:07 -04:00
|
|
|
fn op_encoding_new_decoder(
|
|
|
|
state: &mut OpState,
|
2023-10-05 08:34:38 -04:00
|
|
|
#[string] label: &str,
|
2022-09-01 06:51:13 -04:00
|
|
|
fatal: bool,
|
|
|
|
ignore_bom: bool,
|
2021-06-05 17:10:07 -04:00
|
|
|
) -> Result<ResourceId, AnyError> {
|
|
|
|
let encoding = Encoding::for_label(label.as_bytes()).ok_or_else(|| {
|
|
|
|
range_error(format!(
|
2023-01-27 10:43:16 -05:00
|
|
|
"The encoding label provided ('{label}') is invalid."
|
2021-06-05 17:10:07 -04:00
|
|
|
))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let decoder = if ignore_bom {
|
|
|
|
encoding.new_decoder_without_bom_handling()
|
|
|
|
} else {
|
|
|
|
encoding.new_decoder_with_bom_removal()
|
|
|
|
};
|
|
|
|
|
|
|
|
let rid = state.resource_table.add(TextDecoderResource {
|
|
|
|
decoder: RefCell::new(decoder),
|
|
|
|
fatal,
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(rid)
|
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
|
|
|
#[serde]
|
2021-06-05 17:10:07 -04:00
|
|
|
fn op_encoding_decode(
|
|
|
|
state: &mut OpState,
|
2023-10-05 08:34:38 -04:00
|
|
|
#[anybuffer] data: &[u8],
|
|
|
|
#[smi] rid: ResourceId,
|
2022-09-01 06:51:13 -04:00
|
|
|
stream: bool,
|
2022-03-15 19:22:00 -04:00
|
|
|
) -> Result<U16String, AnyError> {
|
2021-08-15 07:29:19 -04:00
|
|
|
let resource = state.resource_table.get::<TextDecoderResource>(rid)?;
|
2021-06-05 17:10:07 -04:00
|
|
|
|
|
|
|
let mut decoder = resource.decoder.borrow_mut();
|
|
|
|
let fatal = resource.fatal;
|
|
|
|
|
2022-03-15 19:22:00 -04:00
|
|
|
let max_buffer_length = decoder
|
|
|
|
.max_utf16_buffer_length(data.len())
|
|
|
|
.ok_or_else(|| range_error("Value too large to decode."))?;
|
2021-06-05 17:10:07 -04:00
|
|
|
|
2022-04-02 08:37:11 -04:00
|
|
|
let mut output = vec![0; max_buffer_length];
|
2021-06-05 17:10:07 -04:00
|
|
|
|
|
|
|
if fatal {
|
2022-03-15 19:22:00 -04:00
|
|
|
let (result, _, written) =
|
2022-09-07 06:51:47 -04:00
|
|
|
decoder.decode_to_utf16_without_replacement(data, &mut output, !stream);
|
2021-06-05 17:10:07 -04:00
|
|
|
match result {
|
2022-03-15 19:22:00 -04:00
|
|
|
DecoderResult::InputEmpty => {
|
|
|
|
output.truncate(written);
|
2022-04-02 08:37:11 -04:00
|
|
|
Ok(output.into())
|
2022-03-15 19:22:00 -04:00
|
|
|
}
|
2021-06-05 17:10:07 -04:00
|
|
|
DecoderResult::OutputFull => {
|
|
|
|
Err(range_error("Provided buffer too small."))
|
|
|
|
}
|
|
|
|
DecoderResult::Malformed(_, _) => {
|
|
|
|
Err(type_error("The encoded data is not valid."))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2022-03-15 19:22:00 -04:00
|
|
|
let (result, _, written, _) =
|
2022-09-07 06:51:47 -04:00
|
|
|
decoder.decode_to_utf16(data, &mut output, !stream);
|
2021-06-05 17:10:07 -04:00
|
|
|
match result {
|
2022-03-15 19:22:00 -04:00
|
|
|
CoderResult::InputEmpty => {
|
|
|
|
output.truncate(written);
|
2022-04-02 08:37:11 -04:00
|
|
|
Ok(output.into())
|
2022-03-15 19:22:00 -04:00
|
|
|
}
|
2021-06-05 17:10:07 -04:00
|
|
|
CoderResult::OutputFull => Err(range_error("Provided buffer too small.")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TextDecoderResource {
|
|
|
|
decoder: RefCell<Decoder>,
|
|
|
|
fatal: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Resource for TextDecoderResource {
|
|
|
|
fn name(&self) -> Cow<str> {
|
|
|
|
"textDecoder".into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-25 07:43:38 -04:00
|
|
|
#[op2(fast(op_encoding_encode_into_fast))]
|
|
|
|
fn op_encoding_encode_into(
|
2023-03-05 03:00:22 -05:00
|
|
|
scope: &mut v8::HandleScope,
|
2023-10-25 07:43:38 -04:00
|
|
|
input: v8::Local<v8::Value>,
|
|
|
|
#[buffer] buffer: &mut [u8],
|
|
|
|
#[buffer] out_buf: &mut [u32],
|
2023-03-05 03:00:22 -05:00
|
|
|
) -> Result<(), AnyError> {
|
2023-10-25 07:43:38 -04:00
|
|
|
let s = v8::Local::<v8::String>::try_from(input)?;
|
2023-03-05 03:00:22 -05:00
|
|
|
|
|
|
|
let mut nchars = 0;
|
|
|
|
out_buf[1] = s.write_utf8(
|
|
|
|
scope,
|
|
|
|
buffer,
|
|
|
|
Some(&mut nchars),
|
|
|
|
v8::WriteOptions::NO_NULL_TERMINATION
|
|
|
|
| v8::WriteOptions::REPLACE_INVALID_UTF8,
|
|
|
|
) as u32;
|
|
|
|
out_buf[0] = nchars as u32;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-10-25 07:43:38 -04:00
|
|
|
#[op2(fast)]
|
|
|
|
fn op_encoding_encode_into_fast(
|
|
|
|
#[string] input: Cow<'_, str>,
|
|
|
|
#[buffer] buffer: &mut [u8],
|
|
|
|
#[buffer] out_buf: &mut [u32],
|
2023-03-03 08:34:10 -05:00
|
|
|
) {
|
|
|
|
// Since `input` is already UTF-8, we can simply find the last UTF-8 code
|
|
|
|
// point boundary from input that fits in `buffer`, and copy the bytes up to
|
|
|
|
// that point.
|
|
|
|
let boundary = if buffer.len() >= input.len() {
|
|
|
|
input.len()
|
|
|
|
} else {
|
|
|
|
let mut boundary = buffer.len();
|
|
|
|
|
|
|
|
// The maximum length of a UTF-8 code point is 4 bytes.
|
|
|
|
for _ in 0..4 {
|
|
|
|
if input.is_char_boundary(boundary) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
debug_assert!(boundary > 0);
|
|
|
|
boundary -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug_assert!(input.is_char_boundary(boundary));
|
|
|
|
boundary
|
|
|
|
};
|
|
|
|
|
|
|
|
buffer[..boundary].copy_from_slice(input[..boundary].as_bytes());
|
|
|
|
|
|
|
|
// The `read` output parameter is measured in UTF-16 code units.
|
|
|
|
out_buf[0] = match input {
|
|
|
|
// Borrowed Cow strings are zero-copy views into the V8 heap.
|
|
|
|
// Thus, they are guarantee to be SeqOneByteString.
|
|
|
|
Cow::Borrowed(v) => v[..boundary].len() as u32,
|
|
|
|
Cow::Owned(v) => v[..boundary].encode_utf16().count() as u32,
|
|
|
|
};
|
|
|
|
out_buf[1] = boundary as u32;
|
2021-06-05 17:10:07 -04:00
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
2022-10-26 08:58:25 -04:00
|
|
|
fn op_transfer_arraybuffer<'a>(
|
|
|
|
scope: &mut v8::HandleScope<'a>,
|
2023-10-05 08:34:38 -04:00
|
|
|
ab: &v8::ArrayBuffer,
|
|
|
|
) -> Result<v8::Local<'a, v8::ArrayBuffer>, AnyError> {
|
2022-10-26 08:58:25 -04:00
|
|
|
if !ab.is_detachable() {
|
|
|
|
return Err(type_error("ArrayBuffer is not detachable"));
|
|
|
|
}
|
|
|
|
let bs = ab.get_backing_store();
|
2022-11-30 17:20:18 -05:00
|
|
|
ab.detach(None);
|
2023-10-05 08:34:38 -04:00
|
|
|
Ok(v8::ArrayBuffer::with_backing_store(scope, &bs))
|
2022-10-26 08:58:25 -04:00
|
|
|
}
|
|
|
|
|
2022-03-16 20:25:44 -04:00
|
|
|
pub fn get_declaration() -> PathBuf {
|
|
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_web.d.ts")
|
|
|
|
}
|
|
|
|
|
2021-06-05 13:30:20 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct DomExceptionQuotaExceededError {
|
|
|
|
pub msg: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DomExceptionQuotaExceededError {
|
|
|
|
pub fn new(msg: &str) -> Self {
|
|
|
|
DomExceptionQuotaExceededError {
|
|
|
|
msg: msg.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-05 17:10:07 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct DomExceptionInvalidCharacterError {
|
|
|
|
pub msg: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DomExceptionInvalidCharacterError {
|
|
|
|
pub fn new(msg: &str) -> Self {
|
|
|
|
DomExceptionInvalidCharacterError {
|
|
|
|
msg: msg.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-05 13:30:20 -04:00
|
|
|
impl fmt::Display for DomExceptionQuotaExceededError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
f.pad(&self.msg)
|
|
|
|
}
|
|
|
|
}
|
2021-06-05 17:10:07 -04:00
|
|
|
impl fmt::Display for DomExceptionInvalidCharacterError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
f.pad(&self.msg)
|
|
|
|
}
|
|
|
|
}
|
2021-06-05 13:30:20 -04:00
|
|
|
|
|
|
|
impl std::error::Error for DomExceptionQuotaExceededError {}
|
|
|
|
|
2021-06-05 17:10:07 -04:00
|
|
|
impl std::error::Error for DomExceptionInvalidCharacterError {}
|
|
|
|
|
|
|
|
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
|
2021-06-05 13:30:20 -04:00
|
|
|
e.downcast_ref::<DomExceptionQuotaExceededError>()
|
|
|
|
.map(|_| "DOMExceptionQuotaExceededError")
|
2021-06-05 17:10:07 -04:00
|
|
|
.or_else(|| {
|
|
|
|
e.downcast_ref::<DomExceptionInvalidCharacterError>()
|
|
|
|
.map(|_| "DOMExceptionInvalidCharacterError")
|
|
|
|
})
|
2021-06-05 13:30:20 -04:00
|
|
|
}
|
2021-06-10 09:26:10 -04:00
|
|
|
pub struct Location(pub Url);
|