use base64::Engine as _;
use rquickjs::{ArrayBuffer, Ctx, Value};
use super::throw_named;
pub fn buffer_source_bytes(ctx: &Ctx<'_>, value: &Value<'_>) -> rquickjs::Result<Vec<u8>> {
if let Some(ab) = ArrayBuffer::from_value(value.clone()) {
return unsafe { ab.as_bytes() }
.map(<[u8]>::to_vec)
.ok_or_else(|| throw_named(ctx, "TypeError", "detached ArrayBuffer"));
}
if let Some(obj) = value.as_object() {
let buffer: rquickjs::Result<ArrayBuffer<'_>> = obj.get("buffer");
if let Ok(ab) = buffer {
let offset: usize = obj.get("byteOffset")?;
let len: usize = obj.get("byteLength")?;
let bytes = unsafe { ab.as_bytes() }
.ok_or_else(|| throw_named(ctx, "TypeError", "detached ArrayBuffer"))?;
return bytes
.get(offset..offset + len)
.map(<[u8]>::to_vec)
.ok_or_else(|| throw_named(ctx, "TypeError", "view out of bounds"));
}
}
Err(throw_named(
ctx,
"TypeError",
"expected an ArrayBuffer or ArrayBuffer view",
))
}
fn decode(ctx: &Ctx<'_>, s: &str, encoding: &str) -> rquickjs::Result<Vec<u8>> {
match encoding {
"utf8" | "utf-8" => Ok(s.as_bytes().to_vec()),
"base64" => base64::engine::general_purpose::STANDARD
.decode(s)
.map_err(|e| throw_named(ctx, "TypeError", format!("invalid base64: {e}"))),
"hex" => (0..s.len() / 2)
.map(|i| u8::from_str_radix(&s[i * 2..i * 2 + 2], 16))
.collect::<Result<Vec<u8>, _>>()
.map_err(|e| throw_named(ctx, "TypeError", format!("invalid hex: {e}"))),
other => Err(throw_named(
ctx,
"TypeError",
format!("unsupported Buffer encoding {other:?} (utf8 | base64 | hex)"),
)),
}
}
pub fn value_to_bytes<'js>(
ctx: &Ctx<'js>,
value: &Value<'js>,
encoding: Option<&str>,
) -> rquickjs::Result<Vec<u8>> {
if let Some(s) = value.as_string() {
return decode(ctx, &s.to_string()?, encoding.unwrap_or("utf8"));
}
if let Some(obj) = value.as_object() {
if let Some(arr) = obj.as_array() {
let mut out = Vec::with_capacity(arr.len());
for i in 0..arr.len() {
out.push(arr.get::<u8>(i)?);
}
return Ok(out);
}
}
buffer_source_bytes(ctx, value)
}