Skip to content

Commit 7171072

Browse files
committed
JS TextEncoder: encodeInto method
Signed-off-by: Didier Wenzek <[email protected]>
1 parent 604b897 commit 7171072

File tree

2 files changed

+81
-9
lines changed

2 files changed

+81
-9
lines changed

crates/extensions/tedge_flows/src/js_lib/text_encoder.rs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use rquickjs::class::Trace;
22
use rquickjs::Class;
33
use rquickjs::Ctx;
4+
use rquickjs::Exception;
45
use rquickjs::JsLifetime;
6+
use rquickjs::Object;
57
use rquickjs::Result;
68
use rquickjs::TypedArray;
79
use rquickjs::Value;
@@ -28,17 +30,62 @@ impl<'js> TextEncoder {
2830
}
2931

3032
pub fn encode(&self, ctx: Ctx<'js>, text: Value<'js>) -> Result<TypedArray<'js, u8>> {
31-
let string = match text.as_string() {
32-
None => {
33-
if let Some(object) = text.as_object() {
34-
if let Some(bytes) = object.as_typed_array::<u8>() {
35-
return Ok(bytes.clone());
36-
}
37-
}
38-
"".to_string()
33+
let string = Self::string_from(text)?;
34+
TypedArray::new(ctx.clone(), string.as_bytes())
35+
}
36+
37+
#[qjs(rename = "encodeInto")]
38+
pub fn encode_into(
39+
&self,
40+
ctx: Ctx<'js>,
41+
text: Value<'js>,
42+
array: TypedArray<'js, u8>,
43+
) -> Result<Object<'js>> {
44+
let string = Self::string_from(text)?;
45+
let offset: usize = array.get("byteOffset").unwrap_or_default();
46+
let buffer = array
47+
.arraybuffer()?
48+
.as_raw()
49+
.ok_or(Exception::throw_message(&ctx, "ArrayBuffer is detached"))?;
50+
51+
let mut read = 0;
52+
let mut written = 0;
53+
let max_len = buffer.len - offset;
54+
for char in string.chars() {
55+
let len = char.len_utf8();
56+
if written + len > max_len {
57+
break;
3958
}
59+
read += char.len_utf16();
60+
written += len;
61+
}
62+
63+
let bytes = &string.as_bytes()[..written];
64+
unsafe {
65+
let buffer_ptr =
66+
std::slice::from_raw_parts_mut(buffer.ptr.as_ptr().add(offset), written);
67+
buffer_ptr.copy_from_slice(bytes);
68+
}
69+
70+
let obj = Object::new(ctx)?;
71+
obj.set("read", read)?;
72+
obj.set("written", written)?;
73+
Ok(obj)
74+
}
75+
}
76+
77+
impl TextEncoder {
78+
pub fn string_from(text: Value<'_>) -> Result<String> {
79+
let string = match text.as_string() {
80+
None => text
81+
.as_object()
82+
.and_then(|object| object.as_typed_array::<u8>())
83+
.and_then(|array| array.as_bytes())
84+
.and_then(|bytes| std::str::from_utf8(bytes).ok())
85+
.map(|s| s.to_string())
86+
.unwrap_or_default(),
4087
Some(js_string) => js_string.to_string()?,
4188
};
42-
TypedArray::new(ctx.clone(), string.as_bytes())
89+
Ok(string)
4390
}
4491
}

crates/extensions/tedge_flows/src/js_script.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,31 @@ export async function onMessage(message, config) {
420420
);
421421
}
422422

423+
#[tokio::test]
424+
async fn using_text_encoder_into() {
425+
let js = r#"
426+
export async function onMessage(message, config) {
427+
const utf8encoder = new TextEncoder();
428+
const u8array = new Uint8Array(8);
429+
const result = utf8encoder.encodeInto(message.payload, u8array);
430+
console.log(result);
431+
utf8encoder.encodeInto(message.payload, u8array.subarray(4));
432+
return [{topic:"encoded", payload: u8array}];
433+
}
434+
"#;
435+
let (runtime, script) = runtime_with(js).await;
436+
437+
let input = Message::new("decoded", "💖");
438+
let output = Message::new_binary("encoded", [240, 159, 146, 150, 240, 159, 146, 150]);
439+
assert_eq!(
440+
script
441+
.on_message(&runtime, &DateTime::now(), &input)
442+
.await
443+
.unwrap(),
444+
vec![output]
445+
);
446+
}
447+
423448
#[tokio::test]
424449
async fn using_standard_built_in_objects() {
425450
let js = r#"

0 commit comments

Comments
 (0)