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;
|
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
|
|
|
|
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;
|
2024-08-02 10:23:21 -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;
|
2022-03-16 20:25:44 -04:00
|
|
|
use std::path::PathBuf;
|
2023-07-01 18:52:30 -04:00
|
|
|
use std::sync::Arc;
|
2020-08-07 10:55:02 -04:00
|
|
|
|
2024-10-17 15:05:38 -04:00
|
|
|
pub use blob::BlobError;
|
|
|
|
pub use compression::CompressionError;
|
|
|
|
pub use message_port::MessagePortError;
|
|
|
|
pub use stream_resource::StreamResourceError;
|
|
|
|
|
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;
|
2024-03-15 20:59:18 -04:00
|
|
|
pub use crate::message_port::deserialize_js_transferables;
|
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;
|
2024-03-10 19:23:06 -04:00
|
|
|
use crate::message_port::op_message_port_recv_message_sync;
|
2024-03-15 20:59:18 -04:00
|
|
|
pub use crate::message_port::serialize_transferables;
|
2021-07-05 09:34:37 -04:00
|
|
|
pub use crate::message_port::JsMessageData;
|
|
|
|
pub use crate::message_port::MessagePort;
|
2024-03-15 20:59:18 -04:00
|
|
|
pub use crate::message_port::Transferable;
|
2021-06-21 13:53:52 -04:00
|
|
|
|
2024-03-01 13:15:18 -05:00
|
|
|
use crate::timers::op_defer;
|
2022-02-15 06:17:30 -05:00
|
|
|
use crate::timers::op_now;
|
|
|
|
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,
|
2024-03-10 19:23:06 -04:00
|
|
|
op_message_port_recv_message_sync,
|
2023-03-17 14:22:15 -04:00
|
|
|
compression::op_compression_new,
|
|
|
|
compression::op_compression_write,
|
|
|
|
compression::op_compression_finish,
|
|
|
|
op_now<P>,
|
2024-03-01 13:15:18 -05:00
|
|
|
op_defer,
|
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
|
|
|
|
2024-10-17 15:05:38 -04:00
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
pub enum WebError {
|
|
|
|
#[error("Failed to decode base64")]
|
|
|
|
Base64Decode,
|
|
|
|
#[error("The encoding label provided ('{0}') is invalid.")]
|
|
|
|
InvalidEncodingLabel(String),
|
|
|
|
#[error("buffer exceeds maximum length")]
|
|
|
|
BufferTooLong,
|
|
|
|
#[error("Value too large to decode")]
|
|
|
|
ValueTooLarge,
|
|
|
|
#[error("Provided buffer too small")]
|
|
|
|
BufferTooSmall,
|
|
|
|
#[error("The encoded data is not valid")]
|
|
|
|
DataInvalid,
|
|
|
|
#[error(transparent)]
|
|
|
|
DataError(#[from] v8::DataError),
|
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
2024-08-02 10:23:21 -04:00
|
|
|
#[serde]
|
2024-10-17 15:05:38 -04:00
|
|
|
fn op_base64_decode(#[string] input: String) -> Result<ToJsBuffer, WebError> {
|
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);
|
2024-08-02 10:23:21 -04:00
|
|
|
Ok(s.into())
|
2022-03-05 14:12:30 -05:00
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
|
|
|
#[serde]
|
2024-10-17 15:05:38 -04:00
|
|
|
fn op_base64_atob(#[serde] mut s: ByteString) -> Result<ByteString, WebError> {
|
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],
|
2024-10-17 15:05:38 -04:00
|
|
|
) -> Result<usize, WebError> {
|
|
|
|
let decoded = base64_simd::forgiving_decode_inplace(input)
|
|
|
|
.map_err(|_| WebError::Base64Decode)?;
|
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,
|
2024-10-17 15:05:38 -04:00
|
|
|
) -> Result<String, WebError> {
|
2021-06-05 17:10:07 -04:00
|
|
|
let encoding = Encoding::for_label_no_replacement(label.as_bytes())
|
2024-10-17 15:05:38 -04:00
|
|
|
.ok_or(WebError::InvalidEncodingLabel(label))?;
|
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,
|
2024-10-17 15:05:38 -04:00
|
|
|
) -> Result<v8::Local<'a, v8::String>, WebError> {
|
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),
|
2024-10-17 15:05:38 -04:00
|
|
|
None => Err(WebError::BufferTooLong),
|
2022-11-11 09:37:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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,
|
2024-10-17 15:05:38 -04:00
|
|
|
) -> Result<U16String, WebError> {
|
|
|
|
let encoding = Encoding::for_label(label.as_bytes())
|
|
|
|
.ok_or(WebError::InvalidEncodingLabel(label))?;
|
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())
|
2024-10-17 15:05:38 -04:00
|
|
|
.ok_or(WebError::ValueTooLarge)?;
|
2022-05-17 09:52:48 -04:00
|
|
|
|
|
|
|
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())
|
|
|
|
}
|
2024-10-17 15:05:38 -04:00
|
|
|
DecoderResult::OutputFull => Err(WebError::BufferTooSmall),
|
|
|
|
DecoderResult::Malformed(_, _) => Err(WebError::DataInvalid),
|
2022-05-17 09:52:48 -04:00
|
|
|
}
|
|
|
|
} 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())
|
|
|
|
}
|
2024-10-17 15:05:38 -04:00
|
|
|
CoderResult::OutputFull => Err(WebError::BufferTooSmall),
|
2022-05-17 09:52:48 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-06 03:40:17 -04:00
|
|
|
#[op2]
|
|
|
|
#[cppgc]
|
2021-06-05 17:10:07 -04:00
|
|
|
fn op_encoding_new_decoder(
|
2023-10-05 08:34:38 -04:00
|
|
|
#[string] label: &str,
|
2022-09-01 06:51:13 -04:00
|
|
|
fatal: bool,
|
|
|
|
ignore_bom: bool,
|
2024-10-17 15:05:38 -04:00
|
|
|
) -> Result<TextDecoderResource, WebError> {
|
|
|
|
let encoding = Encoding::for_label(label.as_bytes())
|
|
|
|
.ok_or_else(|| WebError::InvalidEncodingLabel(label.to_string()))?;
|
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()
|
|
|
|
};
|
|
|
|
|
2024-08-06 03:40:17 -04:00
|
|
|
Ok(TextDecoderResource {
|
2021-06-05 17:10:07 -04:00
|
|
|
decoder: RefCell::new(decoder),
|
|
|
|
fatal,
|
2024-08-06 03:40:17 -04:00
|
|
|
})
|
2021-06-05 17:10:07 -04:00
|
|
|
}
|
|
|
|
|
2023-10-05 08:34:38 -04:00
|
|
|
#[op2]
|
|
|
|
#[serde]
|
2021-06-05 17:10:07 -04:00
|
|
|
fn op_encoding_decode(
|
2023-10-05 08:34:38 -04:00
|
|
|
#[anybuffer] data: &[u8],
|
2024-08-06 03:40:17 -04:00
|
|
|
#[cppgc] resource: &TextDecoderResource,
|
2022-09-01 06:51:13 -04:00
|
|
|
stream: bool,
|
2024-10-17 15:05:38 -04:00
|
|
|
) -> Result<U16String, WebError> {
|
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())
|
2024-10-17 15:05:38 -04:00
|
|
|
.ok_or(WebError::ValueTooLarge)?;
|
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
|
|
|
}
|
2024-10-17 15:05:38 -04:00
|
|
|
DecoderResult::OutputFull => Err(WebError::BufferTooSmall),
|
|
|
|
DecoderResult::Malformed(_, _) => Err(WebError::DataInvalid),
|
2021-06-05 17:10:07 -04:00
|
|
|
}
|
|
|
|
} 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
|
|
|
}
|
2024-10-17 15:05:38 -04:00
|
|
|
CoderResult::OutputFull => Err(WebError::BufferTooSmall),
|
2021-06-05 17:10:07 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TextDecoderResource {
|
|
|
|
decoder: RefCell<Decoder>,
|
|
|
|
fatal: bool,
|
|
|
|
}
|
|
|
|
|
2024-08-06 03:40:17 -04:00
|
|
|
impl deno_core::GarbageCollected for TextDecoderResource {}
|
2021-06-05 17:10:07 -04:00
|
|
|
|
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],
|
2024-10-17 15:05:38 -04:00
|
|
|
) -> Result<(), WebError> {
|
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
|
|
|
}
|
|
|
|
|
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-10 09:26:10 -04:00
|
|
|
pub struct Location(pub Url);
|