Skip to main content

deno_web/
lib.rs

1// Copyright 2018-2026 the Deno authors. MIT license.
2
3mod blob;
4
5mod broadcast_channel;
6mod compression;
7mod console;
8mod message_port;
9mod stream_resource;
10mod timers;
11mod url;
12mod urlpattern;
13
14use std::borrow::Cow;
15use std::cell::RefCell;
16use std::sync::Arc;
17
18pub use blob::BlobError;
19pub use compression::CompressionError;
20use deno_core::U16String;
21use deno_core::convert::ByteString;
22use deno_core::convert::Uint8Array;
23use deno_core::op2;
24use deno_core::url::Url;
25use deno_core::v8;
26use encoding_rs::CoderResult;
27use encoding_rs::Decoder;
28use encoding_rs::DecoderResult;
29use encoding_rs::Encoding;
30pub use message_port::MessagePortError;
31pub use stream_resource::StreamResourceError;
32
33pub use crate::blob::Blob;
34pub use crate::blob::BlobPart;
35pub use crate::blob::BlobStore;
36pub use crate::blob::InMemoryBlobPart;
37use crate::blob::op_blob_create_object_url;
38use crate::blob::op_blob_create_part;
39use crate::blob::op_blob_from_object_url;
40use crate::blob::op_blob_read_part;
41use crate::blob::op_blob_remove_part;
42use crate::blob::op_blob_revoke_object_url;
43use crate::blob::op_blob_slice_part;
44pub use crate::broadcast_channel::InMemoryBroadcastChannel;
45pub use crate::message_port::JsMessageData;
46pub use crate::message_port::MessagePort;
47pub use crate::message_port::Transferable;
48pub use crate::message_port::create_entangled_message_port;
49pub use crate::message_port::deserialize_js_transferables;
50use crate::message_port::op_message_port_create_entangled;
51use crate::message_port::op_message_port_post_message;
52use crate::message_port::op_message_port_recv_message;
53use crate::message_port::op_message_port_recv_message_sync;
54pub use crate::message_port::serialize_transferables;
55pub use crate::timers::StartTime;
56use crate::timers::op_defer;
57use crate::timers::op_now;
58use crate::timers::op_time_origin;
59
60deno_core::extension!(deno_web,
61  deps = [ deno_webidl ],
62  ops = [
63    op_base64_decode,
64    op_base64_decode_into,
65    op_base64_encode,
66    op_base64_encode_from_buffer,
67    op_base64_atob,
68    op_base64_btoa,
69    op_encoding_normalize_label,
70    op_encoding_decode_single,
71    op_encoding_decode_utf8,
72    op_encoding_new_decoder,
73    op_encoding_decode,
74    op_encoding_encode_into,
75    op_blob_create_part,
76    op_blob_slice_part,
77    op_blob_read_part,
78    op_blob_remove_part,
79    op_blob_create_object_url,
80    op_blob_revoke_object_url,
81    op_blob_from_object_url,
82    op_message_port_create_entangled,
83    op_message_port_post_message,
84    op_message_port_recv_message,
85    op_message_port_recv_message_sync,
86    compression::op_compression_new,
87    compression::op_compression_write,
88    compression::op_compression_finish,
89    op_now,
90    op_time_origin,
91    op_defer,
92    stream_resource::op_readable_stream_resource_allocate,
93    stream_resource::op_readable_stream_resource_allocate_sized,
94    stream_resource::op_readable_stream_resource_get_sink,
95    stream_resource::op_readable_stream_resource_write_error,
96    stream_resource::op_readable_stream_resource_write_buf,
97    stream_resource::op_readable_stream_resource_write_sync,
98    stream_resource::op_readable_stream_resource_close,
99    stream_resource::op_readable_stream_resource_await_close,
100    url::op_url_reparse,
101    url::op_url_parse,
102    url::op_url_get_serialization,
103    url::op_url_parse_with_base,
104    url::op_url_parse_search_params,
105    url::op_url_stringify_search_params,
106    urlpattern::op_urlpattern_parse,
107    urlpattern::op_urlpattern_process_match_input,
108    console::op_preview_entries,
109    broadcast_channel::op_broadcast_subscribe,
110    broadcast_channel::op_broadcast_unsubscribe,
111    broadcast_channel::op_broadcast_send,
112    broadcast_channel::op_broadcast_recv,
113  ],
114  esm = [
115    "00_infra.js",
116    "01_dom_exception.js",
117    "01_mimesniff.js",
118    "02_event.js",
119    "02_structured_clone.js",
120    "02_timers.js",
121    "03_abort_signal.js",
122    "04_global_interfaces.js",
123    "05_base64.js",
124    "06_streams.js",
125    "08_text_encoding.js",
126    "09_file.js",
127    "10_filereader.js",
128    "12_location.js",
129    "13_message_port.js",
130    "14_compression.js",
131    "15_performance.js",
132    "16_image_data.js",
133    "00_url.js",
134    "01_urlpattern.js",
135    "01_console.js",
136    "01_broadcast_channel.js"
137  ],
138  lazy_loaded_esm = [ "webtransport.js" ],
139  options = {
140    blob_store: Arc<BlobStore>,
141    maybe_location: Option<Url>,
142    bc: InMemoryBroadcastChannel,
143  },
144  state = |state, options| {
145    state.put(options.blob_store);
146    if let Some(location) = options.maybe_location {
147      state.put(Location(location));
148    }
149    state.put(StartTime::default());
150    state.put(options.bc);
151  }
152);
153
154#[derive(Debug, thiserror::Error, deno_error::JsError)]
155pub enum WebError {
156  #[class("DOMExceptionInvalidCharacterError")]
157  #[error("Failed to decode base64")]
158  Base64Decode,
159  #[class(range)]
160  #[error("The encoding label provided ('{0}') is invalid.")]
161  InvalidEncodingLabel(String),
162  #[class(type)]
163  #[error("buffer exceeds maximum length")]
164  BufferTooLong,
165  #[class(range)]
166  #[error("Value too large to decode")]
167  ValueTooLarge,
168  #[class(range)]
169  #[error("Provided buffer too small")]
170  BufferTooSmall,
171  #[class(type)]
172  #[error("The encoded data is not valid")]
173  DataInvalid,
174  #[class(generic)]
175  #[error(transparent)]
176  DataError(#[from] v8::DataError),
177}
178
179/// Forgiving base64 decode using simdutf. Decodes into a new Vec.
180/// Handles whitespace stripping and missing padding (Loose mode).
181#[inline]
182fn simdutf_base64_decode_to_vec(input: &[u8]) -> Result<Vec<u8>, WebError> {
183  use v8::simdutf;
184  let max_len = simdutf::maximal_binary_length_from_base64(input);
185  let mut output = Vec::with_capacity(max_len);
186  // Safety: output has max_len bytes of capacity which is >= decoded size.
187  // ffi_base64_to_binary writes into the buffer without reading uninitialized data.
188  let result = unsafe {
189    ffi_base64_to_binary(
190      input.as_ptr(),
191      input.len(),
192      output.as_mut_ptr(),
193      simdutf::Base64Options::Default as u64,
194      simdutf::LastChunkHandling::Loose as u64,
195    )
196  };
197  // error == 0 means success (simdutf error_code::SUCCESS)
198  if result.error != 0 {
199    return Err(WebError::Base64Decode);
200  }
201  // Safety: base64_to_binary wrote result.count bytes.
202  unsafe { output.set_len(result.count) };
203  Ok(output)
204}
205
206/// Forgiving base64 decode into an existing buffer using simdutf.
207/// Returns the number of bytes written.
208#[inline]
209fn simdutf_base64_decode_into(
210  input: &[u8],
211  output: &mut [u8],
212) -> Result<usize, WebError> {
213  use v8::simdutf;
214  // Safety: caller provides output buffer with sufficient capacity.
215  let result = unsafe {
216    simdutf::base64_to_binary(
217      input,
218      output,
219      simdutf::Base64Options::Default,
220      simdutf::LastChunkHandling::Loose,
221    )
222  };
223  if !result.is_ok() {
224    return Err(WebError::Base64Decode);
225  }
226  Ok(result.count)
227}
228
229/// Strict base64 decode directly into target buffer.
230/// Returns None if the input is not valid strict padded base64.
231#[inline]
232fn simdutf_base64_decode_strict(
233  input: &[u8],
234  output: &mut [u8],
235) -> Option<usize> {
236  use v8::simdutf;
237  // Safety: caller provides output buffer with sufficient capacity.
238  let result = unsafe {
239    simdutf::base64_to_binary(
240      input,
241      output,
242      simdutf::Base64Options::Default,
243      simdutf::LastChunkHandling::Strict,
244    )
245  };
246  if result.is_ok() {
247    Some(result.count)
248  } else {
249    None
250  }
251}
252
253// Re-declare simdutf FFI functions to allow passing raw pointers
254// without constructing &mut [u8] from uninitialized memory (which is UB).
255#[repr(C)]
256struct SimdutfFfiResult {
257  error: i32,
258  count: usize,
259}
260
261unsafe extern "C" {
262  #[link_name = "simdutf__binary_to_base64"]
263  fn ffi_binary_to_base64(
264    input: *const u8,
265    length: usize,
266    output: *mut u8,
267    options: u64,
268  ) -> usize;
269
270  #[link_name = "simdutf__base64_to_binary"]
271  fn ffi_base64_to_binary(
272    input: *const u8,
273    length: usize,
274    output: *mut u8,
275    options: u64,
276    last_chunk_options: u64,
277  ) -> SimdutfFfiResult;
278}
279
280/// Encode binary to base64 using simdutf. Returns encoded length.
281///
282/// # Safety
283/// `output` must point to at least `base64_length_from_binary(input.len())`
284/// writable bytes. The bytes do not need to be initialized.
285#[inline]
286unsafe fn simdutf_base64_encode(
287  input: &[u8],
288  output: *mut u8,
289  output_len: usize,
290) -> usize {
291  debug_assert!(
292    output_len
293      >= v8::simdutf::base64_length_from_binary(
294        input.len(),
295        v8::simdutf::Base64Options::Default
296      )
297  );
298  // Safety: caller guarantees output has sufficient capacity.
299  unsafe {
300    ffi_binary_to_base64(
301      input.as_ptr(),
302      input.len(),
303      output,
304      v8::simdutf::Base64Options::Default as u64,
305    )
306  }
307}
308
309#[op2]
310fn op_base64_decode(
311  #[string(onebyte)] input: Cow<[u8]>,
312) -> Result<Uint8Array, WebError> {
313  let v = simdutf_base64_decode_to_vec(&input)?;
314  Ok(v.into())
315}
316
317/// Decode base64 directly into a target buffer at the given offset.
318/// Returns the number of bytes written.
319///
320/// Fast path: tries strict decode directly into target (zero intermediate
321/// copies). This works for properly-padded base64 without whitespace.
322/// Slow path: uses forgiving decode for inputs with whitespace or missing
323/// padding.
324#[op2(fast)]
325fn op_base64_decode_into(
326  #[string(onebyte)] input: Cow<[u8]>,
327  #[buffer] target: &mut [u8],
328  #[smi] offset: u32,
329) -> Result<u32, WebError> {
330  let offset = offset as usize;
331  let target = &mut target[offset..];
332
333  // Fast path: try strict decode directly into target.
334  // Works for clean padded base64 (the common case).
335  let max_len = v8::simdutf::maximal_binary_length_from_base64(&input);
336  if target.len() >= max_len
337    && let Some(len) = simdutf_base64_decode_strict(&input, target)
338  {
339    return Ok(len as u32);
340  }
341
342  // Slow path: forgiving decode for whitespace/missing padding.
343  const STACK_BUF_SIZE: usize = 8192;
344  if max_len <= STACK_BUF_SIZE {
345    let mut buf = std::mem::MaybeUninit::<[u8; STACK_BUF_SIZE]>::uninit();
346    // Safety: simdutf writes into buf without reading uninitialized data.
347    let decoded_len = simdutf_base64_decode_into(&input, unsafe {
348      std::slice::from_raw_parts_mut(
349        buf.as_mut_ptr() as *mut u8,
350        STACK_BUF_SIZE,
351      )
352    })?;
353    let bytes_to_write = decoded_len.min(target.len());
354    // Safety: decoded_len bytes were written by simdutf.
355    target[..bytes_to_write].copy_from_slice(unsafe {
356      std::slice::from_raw_parts(buf.as_ptr() as *const u8, bytes_to_write)
357    });
358    Ok(bytes_to_write as u32)
359  } else {
360    let decoded = simdutf_base64_decode_to_vec(&input)?;
361    let bytes_to_write = decoded.len().min(target.len());
362    target[..bytes_to_write].copy_from_slice(&decoded[..bytes_to_write]);
363    Ok(bytes_to_write as u32)
364  }
365}
366
367#[op2]
368fn op_base64_atob(#[scoped] mut s: ByteString) -> Result<ByteString, WebError> {
369  // Decode into a temporary buffer — simdutf requires non-overlapping buffers.
370  let max_len = v8::simdutf::maximal_binary_length_from_base64(&s);
371  const STACK_BUF_SIZE: usize = 8192;
372  if max_len <= STACK_BUF_SIZE {
373    let mut buf = std::mem::MaybeUninit::<[u8; STACK_BUF_SIZE]>::uninit();
374    // Safety: simdutf writes into buf without reading uninitialized data.
375    let decoded_len = simdutf_base64_decode_into(&s, unsafe {
376      std::slice::from_raw_parts_mut(
377        buf.as_mut_ptr() as *mut u8,
378        STACK_BUF_SIZE,
379      )
380    })?;
381    // Safety: decoded_len bytes were written by simdutf.
382    s[..decoded_len].copy_from_slice(unsafe {
383      std::slice::from_raw_parts(buf.as_ptr() as *const u8, decoded_len)
384    });
385    s.truncate(decoded_len);
386    Ok(s)
387  } else {
388    let decoded = simdutf_base64_decode_to_vec(&s)?;
389    let decoded_len = decoded.len();
390    s[..decoded_len].copy_from_slice(&decoded[..decoded_len]);
391    s.truncate(decoded_len);
392    Ok(s)
393  }
394}
395
396#[op2]
397#[string]
398fn op_base64_encode(#[buffer] s: &[u8]) -> String {
399  forgiving_base64_encode(s)
400}
401
402/// Encode a sub-range of a buffer to base64, avoiding a JS-side slice copy.
403#[op2]
404fn op_base64_encode_from_buffer<'a>(
405  scope: &mut v8::PinScope<'a, '_>,
406  #[buffer] s: &[u8],
407  #[smi] offset: u32,
408  #[smi] length: u32,
409) -> Result<v8::Local<'a, v8::String>, WebError> {
410  let offset = offset as usize;
411  let length = length as usize;
412  let end = (offset + length).min(s.len());
413  base64_encode_to_v8_string(scope, &s[offset..end])
414}
415
416/// Encode bytes to base64 and create a V8 one-byte string directly.
417/// Stack-allocates for inputs producing ≤8KB base64.
418/// Uses v8::String::new_external_onebyte for large outputs to avoid copying.
419#[inline]
420fn base64_encode_to_v8_string<'a>(
421  scope: &mut v8::PinScope<'a, '_>,
422  src: &[u8],
423) -> Result<v8::Local<'a, v8::String>, WebError> {
424  let b64_len = v8::simdutf::base64_length_from_binary(
425    src.len(),
426    v8::simdutf::Base64Options::Default,
427  );
428
429  const STACK_BUF_SIZE: usize = 8192;
430  if b64_len <= STACK_BUF_SIZE {
431    let mut buf = std::mem::MaybeUninit::<[u8; STACK_BUF_SIZE]>::uninit();
432    // Safety: buf has STACK_BUF_SIZE >= b64_len bytes.
433    // simdutf writes `written` bytes without reading uninitialized data.
434    let written = unsafe {
435      simdutf_base64_encode(src, buf.as_mut_ptr() as *mut u8, b64_len)
436    };
437    v8::String::new_from_one_byte(
438      scope,
439      // Safety: written <= b64_len <= STACK_BUF_SIZE, all initialized.
440      unsafe { std::slice::from_raw_parts(buf.as_ptr() as *const u8, written) },
441      v8::NewStringType::Normal,
442    )
443    .ok_or(WebError::BufferTooLong)
444  } else {
445    // Encode into a boxed slice and hand ownership to V8 via external string.
446    // This avoids a copy — V8 will free the buffer when the string is GC'd.
447    let mut buf = Vec::with_capacity(b64_len);
448    // Safety: buf has b64_len bytes of capacity.
449    // binary_to_base64 writes exactly b64_len bytes without reading.
450    let written =
451      unsafe { simdutf_base64_encode(src, buf.as_mut_ptr(), b64_len) };
452    // Safety: written bytes are initialized by binary_to_base64.
453    unsafe { buf.set_len(written) };
454    let buf = buf.into_boxed_slice();
455    debug_assert_eq!(written, b64_len);
456    v8::String::new_external_onebyte(scope, buf).ok_or(WebError::BufferTooLong)
457  }
458}
459
460#[op2]
461#[string]
462fn op_base64_btoa(#[scoped] s: ByteString) -> String {
463  forgiving_base64_encode(s.as_ref())
464}
465
466/// See <https://infra.spec.whatwg.org/#forgiving-base64>
467#[inline]
468pub fn forgiving_base64_encode(s: &[u8]) -> String {
469  let b64_len = v8::simdutf::base64_length_from_binary(
470    s.len(),
471    v8::simdutf::Base64Options::Default,
472  );
473  let mut buf = Vec::with_capacity(b64_len);
474  // Safety: buf has b64_len bytes of capacity.
475  // binary_to_base64 writes up to b64_len bytes, all valid ASCII.
476  unsafe {
477    let written = simdutf_base64_encode(s, buf.as_mut_ptr(), b64_len);
478    buf.set_len(written);
479    String::from_utf8_unchecked(buf)
480  }
481}
482
483#[op2]
484#[string]
485fn op_encoding_normalize_label(
486  #[string] label: String,
487) -> Result<String, WebError> {
488  let encoding = Encoding::for_label_no_replacement(label.as_bytes())
489    .ok_or(WebError::InvalidEncodingLabel(label))?;
490  Ok(encoding.name().to_lowercase())
491}
492
493#[op2]
494fn op_encoding_decode_utf8<'a>(
495  scope: &mut v8::PinScope<'a, '_>,
496  #[anybuffer] zero_copy: &[u8],
497  ignore_bom: bool,
498) -> Result<v8::Local<'a, v8::String>, WebError> {
499  let buf = &zero_copy;
500
501  let buf = if !ignore_bom
502    && buf.len() >= 3
503    && buf[0] == 0xef
504    && buf[1] == 0xbb
505    && buf[2] == 0xbf
506  {
507    &buf[3..]
508  } else {
509    buf
510  };
511
512  // If `String::new_from_utf8()` returns `None`, this means that the
513  // length of the decoded string would be longer than what V8 can
514  // handle. In this case we return `RangeError`.
515  //
516  // For more details see:
517  // - https://encoding.spec.whatwg.org/#dom-textdecoder-decode
518  // - https://github.com/denoland/deno/issues/6649
519  // - https://github.com/v8/v8/blob/d68fb4733e39525f9ff0a9222107c02c28096e2a/include/v8.h#L3277-L3278
520  match v8::String::new_from_utf8(scope, buf, v8::NewStringType::Normal) {
521    Some(text) => Ok(text),
522    None => Err(WebError::BufferTooLong),
523  }
524}
525
526#[op2]
527#[serde]
528fn op_encoding_decode_single(
529  #[anybuffer] data: &[u8],
530  #[string] label: String,
531  fatal: bool,
532  ignore_bom: bool,
533) -> Result<U16String, WebError> {
534  let encoding = Encoding::for_label(label.as_bytes())
535    .ok_or(WebError::InvalidEncodingLabel(label))?;
536
537  let mut decoder = if ignore_bom {
538    encoding.new_decoder_without_bom_handling()
539  } else {
540    encoding.new_decoder_with_bom_removal()
541  };
542
543  let max_buffer_length = decoder
544    .max_utf16_buffer_length(data.len())
545    .ok_or(WebError::ValueTooLarge)?;
546
547  let mut output = vec![0; max_buffer_length];
548
549  if fatal {
550    let (result, _, written) =
551      decoder.decode_to_utf16_without_replacement(data, &mut output, true);
552    match result {
553      DecoderResult::InputEmpty => {
554        output.truncate(written);
555        Ok(output.into())
556      }
557      DecoderResult::OutputFull => Err(WebError::BufferTooSmall),
558      DecoderResult::Malformed(_, _) => Err(WebError::DataInvalid),
559    }
560  } else {
561    let (result, _, written, _) =
562      decoder.decode_to_utf16(data, &mut output, true);
563    match result {
564      CoderResult::InputEmpty => {
565        output.truncate(written);
566        Ok(output.into())
567      }
568      CoderResult::OutputFull => Err(WebError::BufferTooSmall),
569    }
570  }
571}
572
573#[op2]
574#[cppgc]
575fn op_encoding_new_decoder(
576  #[string] label: &str,
577  fatal: bool,
578  ignore_bom: bool,
579) -> Result<TextDecoderResource, WebError> {
580  let encoding = Encoding::for_label(label.as_bytes())
581    .ok_or_else(|| WebError::InvalidEncodingLabel(label.to_string()))?;
582
583  let decoder = if ignore_bom {
584    encoding.new_decoder_without_bom_handling()
585  } else {
586    encoding.new_decoder_with_bom_removal()
587  };
588
589  Ok(TextDecoderResource {
590    decoder: RefCell::new(decoder),
591    fatal,
592  })
593}
594
595#[op2]
596#[serde]
597fn op_encoding_decode(
598  #[anybuffer] data: &[u8],
599  #[cppgc] resource: &TextDecoderResource,
600  stream: bool,
601) -> Result<U16String, WebError> {
602  let mut decoder = resource.decoder.borrow_mut();
603  let fatal = resource.fatal;
604
605  let max_buffer_length = decoder
606    .max_utf16_buffer_length(data.len())
607    .ok_or(WebError::ValueTooLarge)?;
608
609  let mut output = vec![0; max_buffer_length];
610
611  if fatal {
612    let (result, _, written) =
613      decoder.decode_to_utf16_without_replacement(data, &mut output, !stream);
614    match result {
615      DecoderResult::InputEmpty => {
616        output.truncate(written);
617        Ok(output.into())
618      }
619      DecoderResult::OutputFull => Err(WebError::BufferTooSmall),
620      DecoderResult::Malformed(_, _) => Err(WebError::DataInvalid),
621    }
622  } else {
623    let (result, _, written, _) =
624      decoder.decode_to_utf16(data, &mut output, !stream);
625    match result {
626      CoderResult::InputEmpty => {
627        output.truncate(written);
628        Ok(output.into())
629      }
630      CoderResult::OutputFull => Err(WebError::BufferTooSmall),
631    }
632  }
633}
634
635struct TextDecoderResource {
636  decoder: RefCell<Decoder>,
637  fatal: bool,
638}
639
640// SAFETY: we're sure `TextDecoderResource` can be GCed
641unsafe impl deno_core::GarbageCollected for TextDecoderResource {
642  fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}
643
644  fn get_name(&self) -> &'static std::ffi::CStr {
645    c"TextDecoderResource"
646  }
647}
648
649#[op2(fast(op_encoding_encode_into_fast))]
650fn op_encoding_encode_into(
651  scope: &mut v8::PinScope<'_, '_>,
652  input: v8::Local<v8::Value>,
653  #[buffer] buffer: &mut [u8],
654  #[buffer] out_buf: &mut [u32],
655) -> Result<(), WebError> {
656  let s = v8::Local::<v8::String>::try_from(input)?;
657
658  let mut nchars = 0;
659  let len = s.write_utf8_v2(
660    scope,
661    buffer,
662    v8::WriteFlags::kReplaceInvalidUtf8,
663    Some(&mut nchars),
664  );
665  out_buf[1] = len as u32;
666  out_buf[0] = nchars as u32;
667  Ok(())
668}
669
670#[op2(fast)]
671fn op_encoding_encode_into_fast(
672  #[string] input: Cow<'_, str>,
673  #[buffer] buffer: &mut [u8],
674  #[buffer] out_buf: &mut [u32],
675) {
676  // Since `input` is already UTF-8, we can simply find the last UTF-8 code
677  // point boundary from input that fits in `buffer`, and copy the bytes up to
678  // that point.
679  let boundary = if buffer.len() >= input.len() {
680    input.len()
681  } else {
682    let mut boundary = buffer.len();
683
684    // The maximum length of a UTF-8 code point is 4 bytes.
685    for _ in 0..4 {
686      if input.is_char_boundary(boundary) {
687        break;
688      }
689      debug_assert!(boundary > 0);
690      boundary -= 1;
691    }
692
693    debug_assert!(input.is_char_boundary(boundary));
694    boundary
695  };
696
697  buffer[..boundary].copy_from_slice(input[..boundary].as_bytes());
698
699  // The `read` output parameter is measured in UTF-16 code units.
700  out_buf[0] = match input {
701    // Borrowed Cow strings are zero-copy views into the V8 heap.
702    // Thus, they are guarantee to be SeqOneByteString.
703    Cow::Borrowed(v) => v[..boundary].len() as u32,
704    Cow::Owned(v) => v[..boundary].encode_utf16().count() as u32,
705  };
706  out_buf[1] = boundary as u32;
707}
708
709pub struct Location(pub Url);