ferrijs_std/text/
text_encoder.rs1use crate::utils::{bytes::get_lossy_string, result::ResultExt};
4use rquickjs::{
5 atom::PredefinedAtom, function::Opt, Ctx, Exception, Object, Result, TypedArray, Value,
6};
7
8#[derive(rquickjs::class::Trace, rquickjs::JsLifetime)]
9#[rquickjs::class]
10pub struct TextEncoder {}
11
12impl Default for TextEncoder {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18#[rquickjs::methods(rename_all = "camelCase")]
19impl TextEncoder {
20 #[qjs(constructor)]
21 pub fn new() -> Self {
22 Self {}
23 }
24
25 #[qjs(get)]
26 fn encoding(&self) -> &str {
27 "utf-8"
28 }
29
30 #[qjs(prop, rename = PredefinedAtom::SymbolToStringTag, configurable)]
31 pub fn to_string_tag() -> &'static str {
32 stringify!(TextEncoder)
33 }
34
35 pub fn encode<'js>(&self, ctx: Ctx<'js>, string: Opt<Value<'js>>) -> Result<Value<'js>> {
36 if let Some(string) = string.0 {
37 if string.is_string() {
38 let s = get_lossy_string(string)?;
39 return TypedArray::new(ctx.clone(), s.as_bytes())
40 .map(|m: TypedArray<'_, u8>| m.into_value());
41 } else if !string.is_undefined() {
42 return Err(Exception::throw_message(
43 &ctx,
44 "The \"string\" argument must be a string.",
45 ));
46 }
47 }
48
49 TypedArray::new(ctx.clone(), []).map(|m: TypedArray<'_, u8>| m.into_value())
50 }
51
52 pub fn encode_into<'js>(
53 &self,
54 ctx: Ctx<'js>,
55 src: Value<'js>,
56 dst: Value<'js>,
57 ) -> Result<Object<'js>> {
58 if !src.is_string() {
59 return Err(Exception::throw_type(&ctx, "src must be a string"));
60 }
61 let src = get_lossy_string(src)?;
62 let src = src.as_str();
63 if let Ok(typed_array) = TypedArray::<u8>::from_value(dst) {
64 let dst_length = typed_array.len();
65 let dst_offset: usize = typed_array.get("byteOffset")?;
66 let array_buffer = typed_array.arraybuffer()?;
67 let raw = array_buffer
68 .as_raw()
69 .ok_or("ArrayBuffer is detached")
70 .or_throw(&ctx)?;
71
72 let dst = unsafe {
73 std::slice::from_raw_parts_mut(raw.cast::<u8>().as_ptr().add(dst_offset), dst_length)
74 };
75
76 let mut written = 0;
77 let dst_len = dst.len();
78 for ch in src.chars() {
79 let len = ch.len_utf8();
80 if written + len > dst_len {
81 break;
82 }
83 written += len;
84 }
85 dst[..written].copy_from_slice(&src.as_bytes()[..written]);
86 let read: usize = src[..written].chars().map(char::len_utf16).sum();
87
88 let obj = Object::new(ctx)?;
89 obj.set("read", read)?;
90 obj.set("written", written)?;
91 Ok(obj)
92 } else {
93 Err(Exception::throw_type(
94 &ctx,
95 "The \"dest\" argument must be an instance of Uint8Array.",
96 ))
97 }
98 }
99}